intel_th: Perform time resync on capture start

On some devices (TH 2.x devices at the moment), the internal time counter
is initially not synchronized to the global crystal clock, so the time
stamps it produces will not be useful. In this case, the driver needs
to force the time counter resync.

This applies the workaround to relevant devices.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
This commit is contained in:
Alexander Shishkin 2017-02-24 16:09:40 +02:00
parent 29e15e83a9
commit a0e7df335a
5 changed files with 82 additions and 9 deletions

View file

@ -436,8 +436,9 @@ static const struct intel_th_subdevice {
.nres = 1, .nres = 1,
.res = { .res = {
{ {
/* Handle TSCU from GTH driver */
.start = REG_GTH_OFFSET, .start = REG_GTH_OFFSET,
.end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
}, },

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) static int intel_th_gth_reset(struct gth_device *gth)
{ {
u32 scratchpad; u32 reg;
int port, i; int port, i;
scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); reg = ioread32(gth->base + REG_GTH_SCRPD0);
if (scratchpad & SCRPD_DEBUGGER_IN_USE) if (reg & SCRPD_DEBUGGER_IN_USE)
return -EBUSY; return -EBUSY;
/* Always save/restore STH and TU registers in S0ix entry/exit */ /* Always save/restore STH and TU registers in S0ix entry/exit */
scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED;
iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); iowrite32(reg, gth->base + REG_GTH_SCRPD0);
/* output ports */ /* output ports */
for (port = 0; port < 8; port++) { 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); 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 * intel_th_gth_enable() - enable tracing to an output device
* @thdev: GTH 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 intel_th_output *output)
{ {
struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct gth_device *gth = dev_get_drvdata(&thdev->dev);
struct intel_th *th = to_intel_th(thdev);
u32 scr = 0xfc0000, scrpd; u32 scr = 0xfc0000, scrpd;
int master; int master;
@ -539,6 +549,9 @@ static void intel_th_gth_enable(struct intel_th_device *thdev,
output->active = true; output->active = true;
spin_unlock(&gth->gth_lock); spin_unlock(&gth->gth_lock);
if (INTEL_TH_CAP(th, tscu_enable))
gth_tscu_resync(gth);
scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd = ioread32(gth->base + REG_GTH_SCRPD0);
scrpd |= output->scratchpad; scrpd |= output->scratchpad;
iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); iowrite32(scrpd, gth->base + REG_GTH_SCRPD0);

View file

@ -55,9 +55,14 @@ enum {
REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */ REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */
REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */ REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */
REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ 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 */ /* waiting for Pipeline Empty bit(s) to assert for GTH */
#define GTH_PLE_WAITLOOP_DEPTH 10000 #define GTH_PLE_WAITLOOP_DEPTH 10000
#define TSUCTRL_CTCRESYNC BIT(0)
#define TSCUSTAT_CTCSYNCING BIT(1)
#endif /* __INTEL_TH_GTH_H__ */ #endif /* __INTEL_TH_GTH_H__ */

View file

@ -298,6 +298,10 @@ enum {
REG_GTH_OFFSET = 0x0000, REG_GTH_OFFSET = 0x0000,
REG_GTH_LENGTH = 0x2000, REG_GTH_LENGTH = 0x2000,
/* Timestamp counter unit (TSCU) */
REG_TSCU_OFFSET = 0x2000,
REG_TSCU_LENGTH = 0x1000,
/* Software Trace Hub (STH) [0x4000..0x4fff] */ /* Software Trace Hub (STH) [0x4000..0x4fff] */
REG_STH_OFFSET = 0x4000, REG_STH_OFFSET = 0x4000,
REG_STH_LENGTH = 0x2000, REG_STH_LENGTH = 0x2000,

View file

@ -27,6 +27,49 @@
#define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW)) #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, static int intel_th_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
@ -47,6 +90,9 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
if (IS_ERR(th)) if (IS_ERR(th))
return PTR_ERR(th); return PTR_ERR(th);
th->activate = intel_th_pci_activate;
th->deactivate = intel_th_pci_deactivate;
pci_set_master(pdev); pci_set_master(pdev);
return 0; return 0;
@ -59,6 +105,10 @@ static void intel_th_pci_remove(struct pci_dev *pdev)
intel_th_free(th); 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[] = { static const struct pci_device_id intel_th_pci_id_table[] = {
{ {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26),
@ -96,17 +146,17 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
{ {
/* Gemini Lake */ /* Gemini Lake */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e),
.driver_data = (kernel_ulong_t)0, .driver_data = (kernel_ulong_t)&intel_th_2x,
}, },
{ {
/* Cannon Lake H */ /* Cannon Lake H */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326),
.driver_data = (kernel_ulong_t)0, .driver_data = (kernel_ulong_t)&intel_th_2x,
}, },
{ {
/* Cannon Lake LP */ /* Cannon Lake LP */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6),
.driver_data = (kernel_ulong_t)0, .driver_data = (kernel_ulong_t)&intel_th_2x,
}, },
{ 0 }, { 0 },
}; };