diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c index 3a101b61c29..c9da0c6bd03 100644 --- a/drivers/coresight/coresight-etm.c +++ b/drivers/coresight/coresight-etm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -91,75 +91,87 @@ do { \ */ /* Trace registers (0x000-0x2FC) */ -#define ETMCR (0x000) -#define ETMCCR (0x004) -#define ETMTRIGGER (0x008) -#define ETMSR (0x010) -#define ETMSCR (0x014) -#define ETMTSSCR (0x018) -#define ETMTEEVR (0x020) -#define ETMTECR1 (0x024) -#define ETMFFLR (0x02C) -#define ETMACVRn(n) (0x040 + (n * 4)) -#define ETMACTRn(n) (0x080 + (n * 4)) -#define ETMCNTRLDVRn(n) (0x140 + (n * 4)) -#define ETMCNTENRn(n) (0x150 + (n * 4)) -#define ETMCNTRLDEVRn(n) (0x160 + (n * 4)) -#define ETMCNTVRn(n) (0x170 + (n * 4)) -#define ETMSQ12EVR (0x180) -#define ETMSQ21EVR (0x184) -#define ETMSQ23EVR (0x188) -#define ETMSQ31EVR (0x18C) -#define ETMSQ32EVR (0x190) -#define ETMSQ13EVR (0x194) -#define ETMSQR (0x19C) -#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4)) -#define ETMCIDCVRn(n) (0x1B0 + (n * 4)) -#define ETMCIDCMR (0x1BC) -#define ETMIMPSPEC0 (0x1C0) -#define ETMIMPSPEC1 (0x1C4) -#define ETMIMPSPEC2 (0x1C8) -#define ETMIMPSPEC3 (0x1CC) -#define ETMIMPSPEC4 (0x1D0) -#define ETMIMPSPEC5 (0x1D4) -#define ETMIMPSPEC6 (0x1D8) -#define ETMIMPSPEC7 (0x1DC) -#define ETMSYNCFR (0x1E0) -#define ETMIDR (0x1E4) -#define ETMCCER (0x1E8) -#define ETMEXTINSELR (0x1EC) -#define ETMTESSEICR (0x1F0) -#define ETMEIBCR (0x1F4) -#define ETMTSEVR (0x1F8) -#define ETMAUXCR (0x1FC) -#define ETMTRACEIDR (0x200) -#define ETMVMIDCVR (0x240) +#define ETMCR (0x000) +#define ETMCCR (0x004) +#define ETMTRIGGER (0x008) +#define ETMASSICCTLR (0x00C) +#define ETMSR (0x010) +#define ETMSCR (0x014) +#define ETMTSSCR (0x018) +#define ETMTECR2 (0x01C) +#define ETMTEEVR (0x020) +#define ETMTECR1 (0x024) +#define ETMFFLR (0x02C) +#define ETMVDEVR (0x030) +#define ETMVDCR1 (0x034) +#define ETMVDCR3 (0x03C) +#define ETMACVRn(n) (0x040 + (n * 4)) +#define ETMACTRn(n) (0x080 + (n * 4)) +#define ETMDCVRn(n) (0x0C0 + (n * 8)) +#define ETMDCMRn(n) (0x100 + (n * 8)) +#define ETMCNTRLDVRn(n) (0x140 + (n * 4)) +#define ETMCNTENRn(n) (0x150 + (n * 4)) +#define ETMCNTRLDEVRn(n) (0x160 + (n * 4)) +#define ETMCNTVRn(n) (0x170 + (n * 4)) +#define ETMSQ12EVR (0x180) +#define ETMSQ21EVR (0x184) +#define ETMSQ23EVR (0x188) +#define ETMSQ31EVR (0x18C) +#define ETMSQ32EVR (0x190) +#define ETMSQ13EVR (0x194) +#define ETMSQR (0x19C) +#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4)) +#define ETMCIDCVRn(n) (0x1B0 + (n * 4)) +#define ETMCIDCMR (0x1BC) +#define ETMIMPSPEC0 (0x1C0) +#define ETMIMPSPEC1 (0x1C4) +#define ETMIMPSPEC2 (0x1C8) +#define ETMIMPSPEC3 (0x1CC) +#define ETMIMPSPEC4 (0x1D0) +#define ETMIMPSPEC5 (0x1D4) +#define ETMIMPSPEC6 (0x1D8) +#define ETMIMPSPEC7 (0x1DC) +#define ETMSYNCFR (0x1E0) +#define ETMIDR (0x1E4) +#define ETMCCER (0x1E8) +#define ETMEXTINSELR (0x1EC) +#define ETMTESSEICR (0x1F0) +#define ETMEIBCR (0x1F4) +#define ETMTSEVR (0x1F8) +#define ETMAUXCR (0x1FC) +#define ETMTRACEIDR (0x200) +#define ETMIDR2 (0x208) +#define ETMVMIDCVR (0x240) /* Management registers (0x300-0x314) */ -#define ETMOSLAR (0x300) -#define ETMOSLSR (0x304) -#define ETMOSSRR (0x308) -#define ETMPDCR (0x310) -#define ETMPDSR (0x314) +#define ETMOSLAR (0x300) +#define ETMOSLSR (0x304) +#define ETMOSSRR (0x308) +#define ETMPDCR (0x310) +#define ETMPDSR (0x314) -#define ETM_MAX_ADDR_CMP (16) -#define ETM_MAX_CNTR (4) -#define ETM_MAX_CTXID_CMP (3) +#define ETM_MAX_ADDR_CMP (16) +#define ETM_MAX_CNTR (4) +#define ETM_MAX_CTXID_CMP (3) -#define ETM_MODE_EXCLUDE BIT(0) -#define ETM_MODE_CYCACC BIT(1) -#define ETM_MODE_STALL BIT(2) -#define ETM_MODE_TIMESTAMP BIT(3) -#define ETM_MODE_CTXID BIT(4) -#define ETM_MODE_ALL (0x1F) +#define ETM_MODE_EXCLUDE BIT(0) +#define ETM_MODE_CYCACC BIT(1) +#define ETM_MODE_STALL BIT(2) +#define ETM_MODE_TIMESTAMP BIT(3) +#define ETM_MODE_CTXID BIT(4) +#define ETM_MODE_DATA_TRACE_VAL BIT(5) +#define ETM_MODE_DATA_TRACE_ADDR BIT(6) +#define ETM_MODE_ALL (0x7F) -#define ETM_EVENT_MASK (0x1FFFF) -#define ETM_SYNC_MASK (0xFFF) -#define ETM_ALL_MASK (0xFFFFFFFF) +#define ETM_DATACMP_ENABLE (0x2) -#define ETM_SEQ_STATE_MAX_VAL (0x2) +#define ETM_EVENT_MASK (0x1FFFF) +#define ETM_SYNC_MASK (0xFFF) +#define ETM_ALL_MASK (0xFFFFFFFF) -#define ETM_REG_DUMP_VER_OFF (4) -#define ETM_REG_DUMP_VER (1) +#define ETM_SEQ_STATE_MAX_VAL (0x2) + +#define ETM_REG_DUMP_VER_OFF (4) +#define ETM_REG_DUMP_VER (1) enum etm_addr_type { ETM_ADDR_TYPE_NONE, @@ -203,6 +215,7 @@ struct etm_drvdata { uint8_t nr_ext_inp; uint8_t nr_ext_out; uint8_t nr_ctxid_cmp; + uint8_t nr_data_cmp; uint8_t reset; uint32_t mode; uint32_t ctrl; @@ -210,11 +223,18 @@ struct etm_drvdata { uint32_t startstop_ctrl; uint32_t enable_event; uint32_t enable_ctrl1; + uint32_t enable_ctrl2; uint32_t fifofull_level; uint8_t addr_idx; uint32_t addr_val[ETM_MAX_ADDR_CMP]; uint32_t addr_acctype[ETM_MAX_ADDR_CMP]; uint32_t addr_type[ETM_MAX_ADDR_CMP]; + bool data_trace_support; + uint32_t data_val[ETM_MAX_ADDR_CMP]; + uint32_t data_mask[ETM_MAX_ADDR_CMP]; + uint32_t viewdata_event; + uint32_t viewdata_ctrl1; + uint32_t viewdata_ctrl3; uint8_t cntr_idx; uint32_t cntr_rld_val[ETM_MAX_CNTR]; uint32_t cntr_event[ETM_MAX_CNTR]; @@ -247,8 +267,10 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS]; */ static void etm_os_unlock(void *info) { - etm_writel_cp14(0x0, ETMOSLAR); - isb(); + if (cpu_is_krait()) { + etm_writel_cp14(0x0, ETMOSLAR); + isb(); + } } /* @@ -382,6 +404,14 @@ static void etm_disable_pcsave(void *info) ETM_LOCK(drvdata); } +static bool etm_version_gte(uint8_t arch, uint8_t base_arch) +{ + if (arch >= base_arch && ((arch & PFT_ARCH_MAJOR) != PFT_ARCH_MAJOR)) + return true; + else + return false; +} + static void __etm_enable(void *info) { int i; @@ -409,13 +439,24 @@ static void __etm_enable(void *info) etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR); etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER); etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR); + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2)) + etm_writel(drvdata, drvdata->enable_ctrl2, ETMTECR2); etm_writel(drvdata, drvdata->enable_event, ETMTEEVR); etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1); etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR); + if (drvdata->data_trace_support == true) { + etm_writel(drvdata, drvdata->viewdata_event, ETMVDEVR); + etm_writel(drvdata, drvdata->viewdata_ctrl1, ETMVDCR1); + etm_writel(drvdata, drvdata->viewdata_ctrl3, ETMVDCR3); + } for (i = 0; i < drvdata->nr_addr_cmp; i++) { etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i)); etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i)); } + for (i = 0; i < drvdata->nr_data_cmp; i++) { + etm_writel(drvdata, drvdata->data_val[i], ETMDCVRn(i)); + etm_writel(drvdata, drvdata->data_mask[i], ETMDCMRn(i)); + } for (i = 0; i < drvdata->nr_cntr; i++) { etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i)); etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i)); @@ -594,21 +635,37 @@ static ssize_t etm_store_reset(struct device *dev, if (val) { drvdata->mode = ETM_MODE_EXCLUDE; drvdata->ctrl = 0x0; + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) + drvdata->ctrl |= BIT(11); if (cpu_is_krait_v1()) { drvdata->mode |= ETM_MODE_CYCACC; drvdata->ctrl |= BIT(12); } drvdata->trigger_event = 0x406F; drvdata->startstop_ctrl = 0x0; + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2)) + drvdata->enable_ctrl2 = 0x0; drvdata->enable_event = 0x6F; drvdata->enable_ctrl1 = 0x1000000; drvdata->fifofull_level = 0x28; + if (drvdata->data_trace_support == true) { + drvdata->mode |= (ETM_MODE_DATA_TRACE_VAL | + ETM_MODE_DATA_TRACE_ADDR); + drvdata->ctrl |= BIT(2) | BIT(3); + drvdata->viewdata_event = 0x6F; + drvdata->viewdata_ctrl1 = 0x0; + drvdata->viewdata_ctrl3 = 0x10000; + } drvdata->addr_idx = 0x0; for (i = 0; i < drvdata->nr_addr_cmp; i++) { drvdata->addr_val[i] = 0x0; drvdata->addr_acctype[i] = 0x0; drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; } + for (i = 0; i < drvdata->nr_data_cmp; i++) { + drvdata->data_val[i] = 0; + drvdata->data_mask[i] = ~(0); + } drvdata->cntr_idx = 0x0; for (i = 0; i < drvdata->nr_cntr; i++) { drvdata->cntr_rld_val[i] = 0x0; @@ -684,6 +741,17 @@ static ssize_t etm_store_mode(struct device *dev, struct device_attribute *attr, drvdata->ctrl |= (BIT(14) | BIT(15)); else drvdata->ctrl &= ~(BIT(14) | BIT(15)); + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) { + if (drvdata->mode & ETM_MODE_DATA_TRACE_VAL) + drvdata->ctrl |= BIT(2); + else + drvdata->ctrl &= ~(BIT(2)); + + if (drvdata->mode & ETM_MODE_DATA_TRACE_ADDR) + drvdata->ctrl |= (BIT(3)); + else + drvdata->ctrl &= ~(BIT(3)); + } spin_unlock(&drvdata->spinlock); return size; @@ -839,6 +907,8 @@ static ssize_t etm_store_addr_single(struct device *dev, drvdata->addr_val[idx] = val; drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2)) + drvdata->enable_ctrl2 |= (1 << idx); spin_unlock(&drvdata->spinlock); return size; } @@ -1039,6 +1109,138 @@ static ssize_t etm_store_addr_acctype(struct device *dev, static DEVICE_ATTR(addr_acctype, S_IRUGO | S_IWUSR, etm_show_addr_acctype, etm_store_addr_acctype); +static ssize_t etm_show_data_val(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + uint8_t idx; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + idx = idx >> 1; + if (idx >= drvdata->nr_data_cmp) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->data_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t etm_store_data_val(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + uint8_t idx, data_idx; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* Adjust index to use the correct data comparator */ + data_idx = idx >> 1; + /* Only idx = 0, 2, 4, 6... are valid */ + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (data_idx >= drvdata->nr_data_cmp) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!BVAL(drvdata->addr_acctype[idx], ETM_DATACMP_ENABLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE) { + if (!BVAL(drvdata->addr_acctype[idx + 1], ETM_DATACMP_ENABLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + } + + drvdata->data_val[data_idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR(data_val, S_IRUGO | S_IWUSR, etm_show_data_val, + etm_store_data_val); + +static ssize_t etm_show_data_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long mask; + uint8_t idx; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + idx = idx >> 1; + if (idx >= drvdata->nr_data_cmp) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + mask = drvdata->data_mask[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", mask); +} + +static ssize_t etm_store_data_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long mask; + uint8_t idx, data_idx; + + if (sscanf(buf, "%lx", &mask) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* Adjust index to use the correct data comparator */ + data_idx = idx >> 1; + /* Only idx = 0, 2, 4, 6... are valid */ + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (data_idx >= drvdata->nr_data_cmp) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!BVAL(drvdata->addr_acctype[idx], ETM_DATACMP_ENABLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE) { + if (!BVAL(drvdata->addr_acctype[idx + 1], ETM_DATACMP_ENABLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + } + + drvdata->data_mask[data_idx] = mask; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR(data_mask, S_IRUGO | S_IWUSR, etm_show_data_mask, + etm_store_data_mask); + static ssize_t etm_show_cntr_idx(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1604,6 +1806,8 @@ static struct attribute *etm_attrs[] = { &dev_attr_addr_start.attr, &dev_attr_addr_stop.attr, &dev_attr_addr_acctype.attr, + &dev_attr_data_val.attr, + &dev_attr_data_mask.attr, &dev_attr_cntr_idx.attr, &dev_attr_cntr_rld_val.attr, &dev_attr_cntr_event.attr, @@ -1681,6 +1885,8 @@ static bool __devinit etm_arch_supported(uint8_t arch) switch (arch) { case PFT_ARCH_V1_1: break; + case ETM_ARCH_V3_5: + break; default: return false; } @@ -1691,6 +1897,7 @@ static void __devinit etm_init_arch_data(void *info) { uint32_t etmidr; uint32_t etmccr; + uint32_t etmcr; struct etm_drvdata *drvdata = info; ETM_UNLOCK(drvdata); @@ -1721,6 +1928,19 @@ static void __devinit etm_init_arch_data(void *info) drvdata->nr_ext_inp = BMVAL(etmccr, 17, 19); drvdata->nr_ext_out = BMVAL(etmccr, 20, 22); drvdata->nr_ctxid_cmp = BMVAL(etmccr, 24, 25); + drvdata->nr_data_cmp = BMVAL(etmccr, 4, 7); + + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) { + etmcr = etm_readl(drvdata, ETMCR); + etmcr |= (BIT(2) | BIT(3)); + etm_writel(drvdata, etmcr, ETMCR); + etmcr = etm_readl(drvdata, ETMCR); + if (BVAL(etmcr, 2) || BVAL(etmcr, 3)) + drvdata->data_trace_support = true; + else + drvdata->data_trace_support = false; + } else + drvdata->data_trace_support = false; etm_set_pwrdwn(drvdata); ETM_LOCK(drvdata); @@ -1734,6 +1954,8 @@ static void __devinit etm_copy_arch_data(struct etm_drvdata *drvdata) drvdata->nr_ext_inp = etmdrvdata[0]->nr_ext_inp; drvdata->nr_ext_out = etmdrvdata[0]->nr_ext_out; drvdata->nr_ctxid_cmp = etmdrvdata[0]->nr_ctxid_cmp; + drvdata->nr_data_cmp = etmdrvdata[0]->nr_data_cmp; + drvdata->data_trace_support = etmdrvdata[0]->data_trace_support; } static void __devinit etm_init_default_data(struct etm_drvdata *drvdata) @@ -1749,6 +1971,10 @@ static void __devinit etm_init_default_data(struct etm_drvdata *drvdata) drvdata->addr_val[1] = (uint32_t) _etext; drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) { + drvdata->addr_acctype[0] = 0x19; + drvdata->addr_acctype[1] = 0x19; + } } for (i = 0; i < drvdata->nr_cntr; i++) { drvdata->cntr_event[i] = 0x406F; @@ -1781,6 +2007,23 @@ static void __devinit etm_init_default_data(struct etm_drvdata *drvdata) drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; } } + + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) + drvdata->ctrl |= BIT(11); + if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2)) + drvdata->enable_ctrl2 = 0x0; + if (drvdata->data_trace_support == true) { + drvdata->mode |= (ETM_MODE_DATA_TRACE_VAL | + ETM_MODE_DATA_TRACE_ADDR); + drvdata->ctrl |= BIT(2) | BIT(3); + drvdata->viewdata_ctrl1 = 0x0; + drvdata->viewdata_ctrl3 = 0x10000; + drvdata->viewdata_event = 0x6F; + } + for (i = 0; i < drvdata->nr_data_cmp; i++) { + drvdata->data_val[i] = 0; + drvdata->data_mask[i] = ~(0); + } } static int __devinit etm_probe(struct platform_device *pdev) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index f98bd7fe5df..cfc690e8d3c 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -30,7 +30,11 @@ #define CORESIGHT_COMPIDR2 (0xFF8) #define CORESIGHT_COMPIDR3 (0xFFC) +#define ETM_ARCH_V1_0 (0x00) +#define ETM_ARCH_V1_2 (0x02) #define ETM_ARCH_V3_3 (0x23) +#define ETM_ARCH_V3_5 (0x25) +#define PFT_ARCH_MAJOR (0x30) #define PFT_ARCH_V1_1 (0x31) enum coresight_clk_rate {