static int trace_start(struct tracectx *t) { u32 v; unsigned long timeout = TRACER_TIMEOUT; etb_unlock(t); etb_writel(t, 0, ETBR_FORMATTERCTRL); etb_writel(t, 1, ETBR_CTRL); etb_lock(t); /* configure etm */ v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); if (t->flags & TRACER_CYCLE_ACC) v |= ETMCTRL_CYCLEACCURATE; etm_unlock(t); etm_writel(t, v, ETMR_CTRL); while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); etm_lock(t); return -EFAULT; } etm_setup_address_range(t, 1, (unsigned long)_stext, (unsigned long)_etext, 0, 0); etm_writel(t, 0, ETMR_TRACEENCTRL2); etm_writel(t, 0, ETMR_TRACESSCTRL); etm_writel(t, 0x6f, ETMR_TRACEENEVT); v &= ~ETMCTRL_PROGRAM; v |= ETMCTRL_PORTSEL; etm_writel(t, v, ETMR_CTRL); timeout = TRACER_TIMEOUT; while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); etm_lock(t); return -EFAULT; } etm_lock(t); t->flags |= TRACER_RUNNING; return 0; }
static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; int ret = 0; ret = amba_request_regions(dev, NULL); if (ret) goto out; t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); if (!t->etb_regs) { ret = -ENOMEM; goto out_release; } amba_set_drvdata(dev, t); etb_miscdev.parent = &dev->dev; ret = misc_register(&etb_miscdev); if (ret) goto out_unmap; t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); if (IS_ERR(t->emu_clk)) { dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); return -EFAULT; } clk_enable(t->emu_clk); etb_unlock(t); t->etb_bufsz = etb_readl(t, ETBR_DEPTH); dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); /* make sure trace capture is disabled */ etb_writel(t, 0, ETBR_CTRL); etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); etb_lock(t); dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); out: return ret; out_unmap: amba_set_drvdata(dev, NULL); iounmap(t->etb_regs); out_release: amba_release_regions(dev); return ret; }
static ssize_t etb_read(struct file *file, char __user *data, size_t len, loff_t *ppos) { int total, i; long length; struct tracectx *t = file->private_data; u32 first = 0; u32 *buf; mutex_lock(&t->mutex); if (trace_isrunning(t)) { length = 0; goto out; } etb_unlock(t); total = etb_getdatalen(t); if (total == t->etb_bufsz) first = etb_readl(t, ETBR_WRITEADDR); etb_writel(t, first, ETBR_READADDR); length = min(total * 4, (int)len); buf = vmalloc(length); dev_dbg(t->dev, "ETB buffer length: %d\n", total); dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); for (i = 0; i < length / 4; i++) buf[i] = etb_readl(t, ETBR_READMEM); /* the only way to deassert overflow bit in ETB status is this */ etb_writel(t, 1, ETBR_CTRL); etb_writel(t, 0, ETBR_CTRL); etb_writel(t, 0, ETBR_WRITEADDR); etb_writel(t, 0, ETBR_READADDR); etb_writel(t, 0, ETBR_TRIGGERCOUNT); etb_lock(t); length -= copy_to_user(data, buf, length); vfree(buf); out: mutex_unlock(&t->mutex); return length; }
static int etb_probe(struct platform_device *pdev) { struct etb_driver_data *data = dev_get_platdata(&pdev->dev); mutex_lock(&tracer.mutex); tracer.etb_regs = data->etb_regs; tracer.funnel_regs = data->funnel_regs; tracer.tpiu_regs = data->tpiu_regs; tracer.dem_regs = data->dem_regs; tracer.etr_virt = data->etr_virt; tracer.etr_phys = data->etr_phys; tracer.etr_len = data->etr_len; tracer.use_etr = data->use_etr; if (unlikely(misc_register(&etb_device) != 0)) { pr_err("Fail to register etb device\n"); } /* AHBAP_EN to enable master port, then ETR could write the trace to bus */ __raw_writel(DEM_UNLOCK_MAGIC, DEM_UNLOCK); mt65xx_reg_sync_writel(AHB_EN, AHBAP_EN); etb_unlock(&tracer); if (tracer.use_etr) { pr_info("ETR virt = 0x%x, phys = 0x%x\n", tracer.etr_virt, tracer.etr_phys); /* Set up ETR memory buffer address */ etb_writel(&tracer, tracer.etr_phys, 0x118); /* Set up ETR memory buffer size */ etb_writel(&tracer, tracer.etr_len, 0x4); } /* Disable ETB capture (ETB_CTL bit0 = 0x0) */ /* For wdt reset */ cs_cpu_write(tracer.etb_regs, 0x20, 0x0); tracer.etb_total_buf_size = etb_readl(&tracer, ETBRDP); tracer.state = TRACE_STATE_STOP; mutex_unlock(&tracer.mutex); return 0; }
/* sysrq+v will always stop the running trace and leave it at that */ static void etm_dump(void) { struct tracectx *t = &tracer; u32 first = 0; int length; if (!t->etb_regs) { printk(KERN_INFO "No tracing hardware found\n"); return; } if (trace_isrunning(t)) trace_stop(t); etb_unlock(t); length = etb_getdatalen(t); if (length == t->etb_bufsz) first = etb_readl(t, ETBR_WRITEADDR); etb_writel(t, first, ETBR_READADDR); printk(KERN_INFO "Trace buffer contents length: %d\n", length); printk(KERN_INFO "--- ETB buffer begin ---\n"); for (; length; length--) printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); printk(KERN_INFO "\n--- ETB buffer end ---\n"); /* deassert the overflow bit */ etb_writel(t, 1, ETBR_CTRL); etb_writel(t, 0, ETBR_CTRL); etb_writel(t, 0, ETBR_TRIGGERCOUNT); etb_writel(t, 0, ETBR_READADDR); etb_writel(t, 0, ETBR_WRITEADDR); etb_lock(t); }
static int trace_stop(struct tracectx *t) { unsigned long timeout = TRACER_TIMEOUT; etm_unlock(t); etm_writel(t, 0x440, ETMR_CTRL); while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); etm_lock(t); return -EFAULT; } etm_lock(t); etb_unlock(t); etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); timeout = TRACER_TIMEOUT; while (etb_readl(t, ETBR_FORMATTERCTRL) & ETBFF_MANUAL_FLUSH && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for formatter flush to commence " "timed out\n"); etb_lock(t); return -EFAULT; } etb_writel(t, 0, ETBR_CTRL); etb_lock(t); t->flags &= ~TRACER_RUNNING; return 0; }
static ssize_t trace_info_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; int datalen; etb_unlock(&tracer); datalen = etb_getdatalen(&tracer); etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); etb_ra = etb_readl(&tracer, ETBR_READADDR); etb_st = etb_readl(&tracer, ETBR_STATUS); etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); etb_lock(&tracer); etm_unlock(&tracer); etm_ctrl = etm_readl(&tracer, ETMR_CTRL); etm_st = etm_readl(&tracer, ETMR_STATUS); etm_lock(&tracer); return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" "ETBR_WRITEADDR:\t%08x\n" "ETBR_READADDR:\t%08x\n" "ETBR_STATUS:\t%08x\n" "ETBR_FORMATTERCTRL:\t%08x\n" "ETMR_CTRL:\t%08x\n" "ETMR_STATUS:\t%08x\n", datalen, tracer.ncmppairs, etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st ); }
/* * trace_start_by_cpus: Restart traces of the given CPUs. * @mask: cpu mask * @init_etb: a flag to re-initialize ETB, funnel, ... etc */ void trace_start_by_cpus(const struct cpumask *mask, int init_etb) { int i; if (!mask) { return ; } if (init_etb) { /* enable master port such that ETR could write the trace to bus */ mt65xx_reg_sync_writel(AHB_EN, (volatile u32 *)AHBAP_EN); etb_unlock(&tracer); if (tracer.use_etr) { /* Set up ETR memory buffer address */ etb_writel(&tracer, tracer.etr_phys, 0x118); /* Set up ETR memory buffer size */ etb_writel(&tracer, tracer.etr_len, 0x4); } /* Disable ETB capture (ETB_CTL bit0 = 0x0) */ /* For wdt reset */ cs_cpu_write (tracer.etb_regs, 0x20, 0x0); } for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_unlock(tracer.etm_regs[i]); } } if (init_etb) { cs_cpu_unlock(tracer.tpiu_regs); cs_cpu_unlock(tracer.funnel_regs); cs_cpu_unlock(tracer.etb_regs); cs_cpu_funnel_setup(); cs_cpu_etb_setup(); } /* Power-up PTMs */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_ptm_powerup(tracer.etm_regs[i]); } } /* Disable PTMs so that they can be set up safely */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_ptm_progbit(tracer.etm_regs[i]); } } /* Set up PTMs */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_test_common_ptm_setup(tracer.etm_regs[i], i); } } /* Set up CoreSightTraceID */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_write(tracer.etm_regs[i], 0x200, i + 1); } } /* Enable PTMs now everything has been set up */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (cpumask_test_cpu(i, mask)) { cs_cpu_ptm_clear_progbit(tracer.etm_regs[i]); } } if (init_etb) { /* Avoid DBG_sys being reset */ mt65xx_reg_sync_writel(DEM_UNLOCK_MAGIC, (volatile u32 *)DEM_UNLOCK); mt65xx_reg_sync_writel(POWER_ON_RESET, (volatile u32 *)DBGRST_ALL); mt65xx_reg_sync_writel(BUSCLK_EN, (volatile u32 *)DBGBUSCLK_EN); mt65xx_reg_sync_writel(SYSCLK_EN, (volatile u32 *)DBGSYSCLK_EN); etb_lock(&tracer); } }
static void trace_stop(void) { int i; int pwr_down; if (tracer.state == TRACE_STATE_STOP) { pr_info("ETM trace is already stop!\n"); return; } get_online_cpus(); mutex_lock(&tracer.mutex); etb_unlock(&tracer); /* "Trace program done" */ /* "Disable trace components" */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_ptm_progbit(tracer.etm_regs[i]); } } #if 0 cs_cpu_flushandstop(tracer.tpiu_regs); #endif /* Disable ETB capture (ETB_CTL bit0 = 0x0) */ cs_cpu_write(tracer.etb_regs, 0x20, 0x0); /* Reset ETB RAM Read Data Pointer (ETB_RRP = 0x0) */ /* no need to reset RRP */ #if 0 cs_cpu_write (tracer.etb_regs, 0x14, 0x0); #endif /* power down */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_write(tracer.etm_regs[i], 0x0, 0x1); } } dsb(); tracer.state = TRACE_STATE_STOP; etb_lock(&tracer); mutex_unlock(&tracer.mutex); put_online_cpus(); }
static void trace_start(void) { int i; int pwr_down; if (tracer.state == TRACE_STATE_TRACING) { pr_info("ETM trace is already running\n"); return; } get_online_cpus(); mutex_lock(&tracer.mutex); /* AHBAP_EN to enable master port, then ETR could write the trace to bus */ __raw_writel(DEM_UNLOCK_MAGIC, DEM_UNLOCK); mt65xx_reg_sync_writel(AHB_EN, AHBAP_EN); etb_unlock(&tracer); for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_unlock(tracer.etm_regs[i]); } } cs_cpu_unlock(tracer.tpiu_regs); cs_cpu_unlock(tracer.funnel_regs); cs_cpu_unlock(tracer.etb_regs); cs_cpu_funnel_setup(); cs_cpu_etb_setup(); /* Power-up TMs */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_ptm_powerup(tracer.etm_regs[i]); } } /* Disable TMs so that they can be set up safely */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_ptm_progbit(tracer.etm_regs[i]); } } /* Set up TMs */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_test_common_ptm_setup(tracer.etm_regs[i], i); } } /* Set up CoreSightTraceID */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_write(tracer.etm_regs[i], 0x200, i + 1); } } /* update the ETMCR and ETMCCER */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { tracer.etm_info[i].etmcr = etm_readl(&tracer, i, ETMCR); tracer.etm_info[i].etmccer = etm_readl(&tracer, i, ETMCCER); } } /* Enable TMs now everything has been set up */ for (i = 0; i < tracer.nr_etm_regs; i++) { if (tracer.etm_info[i].pwr_down == NULL) { pwr_down = 0; } else { pwr_down = *(tracer.etm_info[i].pwr_down); } if (!pwr_down) { cs_cpu_ptm_clear_progbit(tracer.etm_regs[i]); } } /* Avoid DBG_sys being reset */ __raw_writel(DEM_UNLOCK_MAGIC, DEM_UNLOCK); __raw_writel(POWER_ON_RESET, DBGRST_ALL); __raw_writel(BUSCLK_EN, DBGBUSCLK_EN); mt65xx_reg_sync_writel(SYSCLK_EN, DBGSYSCLK_EN); tracer.state = TRACE_STATE_TRACING; etb_lock(&tracer); mutex_unlock(&tracer.mutex); put_online_cpus(); }
static ssize_t etb_read(struct file *file, char __user *data, size_t len, loff_t *ppos) { int total, i; long length; struct etm_trace_context_t *t = file->private_data; u32 first = 0, buffer_end = 0; u32 *buf; int wpos; int skip; long wlength; loff_t pos = *ppos; mutex_lock(&t->mutex); if (t->state == TRACE_STATE_TRACING) { length = 0; pr_err("Need to stop trace\n"); goto out; } etb_unlock(t); total = etb_get_data_length(t); if (total == t->etb_total_buf_size) { first = etb_readl(t, ETBRWP); if (t->use_etr) { first = (first - t->etr_phys) / 4; } } if (pos > total * 4) { skip = 0; wpos = total; } else { skip = (int)pos % 4; wpos = (int)pos / 4; } total -= wpos; first = (first + wpos) % t->etb_total_buf_size; etb_writel(t, first, ETBRRP); wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); length = min(total * 4 - skip, (int)len); if (wlength == 0) { goto out; } buf = vmalloc(wlength * 4); pr_info("ETB read %ld bytes to %lld from %ld words at %d\n", length, pos, wlength, first); pr_info("ETB buffer length: %d\n", total + wpos); pr_info("ETB status reg: 0x%x\n", etb_readl(t, ETBSTS)); if (t->use_etr) { /* * XXX: ETBRRP cannot wrap around correctly on ETR. * The workaround is to read the buffer from WTBRWP directly. */ pr_info("ETR virt = 0x%x, phys = 0x%x\n", t->etr_virt, t->etr_phys); /* translate first and buffer_end from phys to virt */ first *= 4; first += t->etr_virt; buffer_end = t->etr_virt + (t->etr_len * 4); pr_info("first(virt) = 0x%x\n\n", first); for (i = 0; i < wlength; i++) { buf[i] = *((unsigned int*)(first)); first += 4; if (first >= buffer_end) { first = t->etr_virt; } } } else { for (i = 0; i < wlength; i++) { buf[i] = etb_readl(t, ETBRRD); } } etb_lock(t); length -= copy_to_user(data, (u8 *)buf + skip, length); vfree(buf); *ppos = pos + length; out: mutex_unlock(&t->mutex); return length; }