mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
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 -----BEGIN PGP SIGNATURE----- iIIEABEIACoFAlgvHY8jHGFsZXhhbmRlci5zaGlzaGtpbkBsaW51eC5pbnRlbC5j b20ACgkQ68X2JGMFV/8SEAD/aBk+2zVndY2qSmLg5rZtqKQj/xjAXHhdEB86JT6W DvUA/RrWFQ1K4s9aOjpjHXDi91w8AoBqCBxXt56r45xxeuKc =VhIw -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
38d1790644
@ -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
|
|||||||
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.
|
||||||
|
|
||||||
For kernel-based trace sources, there is "stm_source" device
|
|
||||||
class. Devices of this class can be connected and disconnected to/from
|
|
||||||
stm devices at runtime via a sysfs attribute.
|
|
||||||
|
|
||||||
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
|
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
|
||||||
[2].
|
[2].
|
||||||
|
|
||||||
|
stm_source
|
||||||
|
==========
|
||||||
|
|
||||||
|
For kernel-based trace sources, there is "stm_source" device
|
||||||
|
class. Devices of this class can be connected and disconnected to/from
|
||||||
|
stm devices at runtime via a sysfs attribute called "stm_source_link"
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 @@
|
|||||||
#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)
|
|||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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(>h->gth_lock);
|
spin_lock(>h->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)
|
|||||||
gth->base = base;
|
gth->base = base;
|
||||||
spin_lock_init(>h->gth_lock);
|
spin_lock_init(>h->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)
|
|||||||
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 {
|
|||||||
* @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 {
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user