Commit 981b4677 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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

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.
parents 234b7f8d a0e7df33
......@@ -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
This diff is collapsed.
......@@ -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;
}
......
......@@ -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}
......@@ -60,6 +71,7 @@ struct intel_th_output {
*/
struct intel_th_device {
struct device dev;
struct intel_th_drvdata *drvdata;
struct resource *resource;
unsigned int num_resources;
unsigned int type;
......@@ -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 },
};
......
/*
* 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;
/*
......
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