Commit 38d17906 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'stm-for-greg-20161118' of...

Merge tag 'stm-for-greg-20161118' 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.10

These are:
  * Fix for an error-path leak in stm
  * Host-driven mode in intel_th
  * Documentation and other small updates
parents 7c7a6077 ee01aebb
...@@ -97,3 +97,25 @@ $ echo 0 > /sys/bus/intel_th/devices/0-msc0/active ...@@ -97,3 +97,25 @@ $ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
# and now you can collect the trace from the device node: # and now you can collect the trace from the device node:
$ cat /dev/intel_th0/msc0 > my_stp_trace $ cat /dev/intel_th0/msc0 > my_stp_trace
Host Debugger Mode
==================
It is possible to configure the Trace Hub and control its trace
capture from a remote debug host, which should be connected via one of
the hardware debugging interfaces, which will then be used to both
control Intel Trace Hub and transfer its trace data to the debug host.
The driver needs to be told that such an arrangement is taking place
so that it does not touch any capture/port configuration and avoids
conflicting with the debug host's configuration accesses. The only
activity that the driver will perform in this mode is collecting
software traces to the Software Trace Hub (an stm class device). The
user is still responsible for setting up adequate master/channel
mappings that the decoder on the receiving end would recognize.
In order to enable the host mode, set the 'host_mode' parameter of the
'intel_th' kernel module to 'y'. None of the virtual output devices
will show up on the intel_th bus. Also, trace configuration and
capture controlling attribute groups of the 'gth' device will not be
exposed. The 'sth' device will operate as usual.
...@@ -69,12 +69,43 @@ stm device's channel mmio region is 64 bytes and hardware page size is ...@@ -69,12 +69,43 @@ stm device's channel mmio region is 64 bytes and hardware page size is
width==64, you should be able to mmap() one page on this file width==64, you should be able to mmap() one page on this file
descriptor and obtain direct access to an mmio region for 64 channels. descriptor and obtain direct access to an mmio region for 64 channels.
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
[2].
stm_source
==========
For kernel-based trace sources, there is "stm_source" device For kernel-based trace sources, there is "stm_source" device
class. Devices of this class can be connected and disconnected to/from class. Devices of this class can be connected and disconnected to/from
stm devices at runtime via a sysfs attribute. stm devices at runtime via a sysfs attribute called "stm_source_link"
by writing the name of the desired stm device there, for example:
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM $ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link
[2].
For examples on how to use stm_source interface in the kernel, refer
to stm_console or stm_heartbeat 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
allocated for the device according to the policy configuration. If
there's a node in the root of the policy directory that matches the
stm_source device's name (for example, "console"), this node will be
used to allocate master and channel numbers. If there's no such policy
node, the stm core will pick the first contiguous chunk of channels
within the first available master. Note that the node must exist
before the stm_source device is connected to its stm device.
stm_console
===========
One implementation of this interface also used in the example above is
the "stm_console" driver, which basically provides a one-way console
for kernel messages over an stm device.
To configure the master/channel pair that will be assigned to this
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.
[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf [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 [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
#include "intel_th.h" #include "intel_th.h"
#include "debug.h" #include "debug.h"
static bool host_mode __read_mostly;
module_param(host_mode, bool, 0444);
static DEFINE_IDA(intel_th_ida); static DEFINE_IDA(intel_th_ida);
static int intel_th_match(struct device *dev, struct device_driver *driver) static int intel_th_match(struct device *dev, struct device_driver *driver)
...@@ -380,7 +383,7 @@ static void intel_th_device_free(struct intel_th_device *thdev) ...@@ -380,7 +383,7 @@ static void intel_th_device_free(struct intel_th_device *thdev)
/* /*
* Intel(R) Trace Hub subdevices * Intel(R) Trace Hub subdevices
*/ */
static struct intel_th_subdevice { static const struct intel_th_subdevice {
const char *name; const char *name;
struct resource res[3]; struct resource res[3];
unsigned nres; unsigned nres;
...@@ -527,14 +530,19 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, ...@@ -527,14 +530,19 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
{ {
struct resource res[3]; struct resource res[3];
unsigned int req = 0; unsigned int req = 0;
int i, err; int src, dst, err;
/* create devices for each intel_th_subdevice */ /* create devices for each intel_th_subdevice */
for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) { for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) {
struct intel_th_subdevice *subdev = &intel_th_subdevices[i]; const struct intel_th_subdevice *subdev =
&intel_th_subdevices[src];
struct intel_th_device *thdev; struct intel_th_device *thdev;
int r; 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, thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
subdev->id); subdev->id);
if (!thdev) { if (!thdev) {
...@@ -577,10 +585,12 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, ...@@ -577,10 +585,12 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
} }
if (subdev->type == INTEL_TH_OUTPUT) { if (subdev->type == INTEL_TH_OUTPUT) {
thdev->dev.devt = MKDEV(th->major, i); thdev->dev.devt = MKDEV(th->major, dst);
thdev->output.type = subdev->otype; thdev->output.type = subdev->otype;
thdev->output.port = -1; thdev->output.port = -1;
thdev->output.scratchpad = subdev->scrpd; thdev->output.scratchpad = subdev->scrpd;
} else if (subdev->type == INTEL_TH_SWITCH) {
thdev->host_mode = host_mode;
} }
err = device_add(&thdev->dev); err = device_add(&thdev->dev);
...@@ -597,14 +607,14 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, ...@@ -597,14 +607,14 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
req++; req++;
} }
th->thdev[i] = thdev; th->thdev[dst++] = thdev;
} }
return 0; return 0;
kill_subdevs: kill_subdevs:
for (i-- ; i >= 0; i--) for (; dst >= 0; dst--)
intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->thdev[dst]);
return err; return err;
} }
...@@ -717,7 +727,7 @@ void intel_th_free(struct intel_th *th) ...@@ -717,7 +727,7 @@ void intel_th_free(struct intel_th *th)
intel_th_request_hub_module_flush(th); intel_th_request_hub_module_flush(th);
for (i = 0; i < TH_SUBDEVICE_MAX; i++) for (i = 0; i < TH_SUBDEVICE_MAX; i++)
if (th->thdev[i] != th->hub) if (th->thdev[i] && th->thdev[i] != th->hub)
intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->thdev[i]);
intel_th_device_remove(th->hub); intel_th_device_remove(th->hub);
......
...@@ -564,6 +564,9 @@ static int intel_th_gth_assign(struct intel_th_device *thdev, ...@@ -564,6 +564,9 @@ static int intel_th_gth_assign(struct intel_th_device *thdev,
struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct gth_device *gth = dev_get_drvdata(&thdev->dev);
int i, id; int i, id;
if (thdev->host_mode)
return -EBUSY;
if (othdev->type != INTEL_TH_OUTPUT) if (othdev->type != INTEL_TH_OUTPUT)
return -EINVAL; return -EINVAL;
...@@ -600,6 +603,9 @@ static void intel_th_gth_unassign(struct intel_th_device *thdev, ...@@ -600,6 +603,9 @@ static void intel_th_gth_unassign(struct intel_th_device *thdev,
struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct gth_device *gth = dev_get_drvdata(&thdev->dev);
int port = othdev->output.port; int port = othdev->output.port;
if (thdev->host_mode)
return;
spin_lock(&gth->gth_lock); spin_lock(&gth->gth_lock);
othdev->output.port = -1; othdev->output.port = -1;
othdev->output.active = false; othdev->output.active = false;
...@@ -654,9 +660,24 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) ...@@ -654,9 +660,24 @@ static int intel_th_gth_probe(struct intel_th_device *thdev)
gth->base = base; gth->base = base;
spin_lock_init(&gth->gth_lock); spin_lock_init(&gth->gth_lock);
/*
* 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
* capture configuration attributes. Also, refuse to assign output
* drivers to ports, see intel_th_gth_assign().
*/
if (thdev->host_mode)
goto done;
ret = intel_th_gth_reset(gth); ret = intel_th_gth_reset(gth);
if (ret) if (ret) {
return ret; if (ret != -EBUSY)
return ret;
thdev->host_mode = true;
goto done;
}
for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++)
gth->master[i] = -1; gth->master[i] = -1;
...@@ -677,6 +698,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) ...@@ -677,6 +698,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev)
return -ENOMEM; return -ENOMEM;
} }
done:
dev_set_drvdata(dev, gth); dev_set_drvdata(dev, gth);
return 0; return 0;
......
...@@ -54,6 +54,7 @@ struct intel_th_output { ...@@ -54,6 +54,7 @@ struct intel_th_output {
* @num_resources: number of resources in @resource array * @num_resources: number of resources in @resource array
* @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH}
* @id: device instance or -1 * @id: device instance or -1
* @host_mode: Intel TH is controlled by an external debug host
* @output: output descriptor for INTEL_TH_OUTPUT devices * @output: output descriptor for INTEL_TH_OUTPUT devices
* @name: device name to match the driver * @name: device name to match the driver
*/ */
...@@ -64,6 +65,9 @@ struct intel_th_device { ...@@ -64,6 +65,9 @@ struct intel_th_device {
unsigned int type; unsigned int type;
int id; int id;
/* INTEL_TH_SWITCH specific */
bool host_mode;
/* INTEL_TH_OUTPUT specific */ /* INTEL_TH_OUTPUT specific */
struct intel_th_output output; struct intel_th_output output;
......
...@@ -361,7 +361,7 @@ static int stm_char_open(struct inode *inode, struct file *file) ...@@ -361,7 +361,7 @@ static int stm_char_open(struct inode *inode, struct file *file)
struct stm_file *stmf; struct stm_file *stmf;
struct device *dev; struct device *dev;
unsigned int major = imajor(inode); unsigned int major = imajor(inode);
int err = -ENODEV; int err = -ENOMEM;
dev = class_find_device(&stm_class, NULL, &major, major_match); dev = class_find_device(&stm_class, NULL, &major, major_match);
if (!dev) if (!dev)
...@@ -369,8 +369,9 @@ static int stm_char_open(struct inode *inode, struct file *file) ...@@ -369,8 +369,9 @@ static int stm_char_open(struct inode *inode, struct file *file)
stmf = kzalloc(sizeof(*stmf), GFP_KERNEL); stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
if (!stmf) if (!stmf)
return -ENOMEM; goto err_put_device;
err = -ENODEV;
stm_output_init(&stmf->output); stm_output_init(&stmf->output);
stmf->stm = to_stm_device(dev); stmf->stm = to_stm_device(dev);
...@@ -382,9 +383,10 @@ static int stm_char_open(struct inode *inode, struct file *file) ...@@ -382,9 +383,10 @@ static int stm_char_open(struct inode *inode, struct file *file)
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
err_free: err_free:
kfree(stmf);
err_put_device:
/* matches class_find_device() above */ /* matches class_find_device() above */
put_device(dev); put_device(dev);
kfree(stmf);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment