/* Copyright (c) 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 * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Coresight management registers */ #define CORESIGHT_ITCTRL (0xF00) #define CORESIGHT_CLAIMSET (0xFA0) #define CORESIGHT_CLAIMCLR (0xFA4) #define CORESIGHT_LAR (0xFB0) #define CORESIGHT_LSR (0xFB4) #define CORESIGHT_AUTHSTATUS (0xFB8) #define CORESIGHT_DEVID (0xFC8) #define CORESIGHT_DEVTYPE (0xFCC) #define CORESIGHT_UNLOCK (0xC5ACCE55) #define TIMEOUT_US (100) #define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb)) #define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb) #define BVAL(val, n) ((val & BIT(n)) >> n) /* Trace registers */ #define ETMCR (0x000) #define ETMCCR (0x004) #define ETMTRIGGER (0x008) #define ETMASICCTLR (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) #define ETMCLAIMSET (0xFA0) #define ETMCLAIMCLR (0xFA4) /* ETM Management registers */ #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) /* DBG Registers */ #define DBGDIDR (0x0) #define DBGWFAR (0x18) #define DBGVCR (0x1C) #define DBGDTRRXext (0x80) #define DBGDSCRext (0x88) #define DBGDTRTXext (0x8C) #define DBGDRCR (0x90) #define DBGBVRn(n) (0x100 + (n * 4)) #define DBGBCRn(n) (0x140 + (n * 4)) #define DBGWVRn(n) (0x180 + (n * 4)) #define DBGWCRn(n) (0x1C0 + (n * 4)) #define DBGPRCR (0x310) #define DBGITMISCOUT (0xEF8) #define DBGITMISCIN (0xEFC) #define DBGCLAIMSET (0xFA0) #define DBGCLAIMCLR (0xFA4) #define DBGDSCR_MASK (0x6C30FC3C) #define MAX_DBG_STATE_SIZE (90) #define MAX_ETM_STATE_SIZE (78) #define TZ_DBG_ETM_FEAT_ID (0x8) #define TZ_DBG_ETM_VER (0x400000) #define ARCH_V3_5 (0x25) #define ARM_DEBUG_ARCH_V7B (0x3) #define etm_write(etm, val, off) \ __raw_writel(val, etm->base + off) #define etm_read(etm, off) \ __raw_readl(etm->base + off) #define dbg_write(dbg, val, off) \ __raw_writel(val, dbg->base + off) #define dbg_read(dbg, off) \ __raw_readl(dbg->base + off) #define ETM_LOCK(base) \ do { \ /* recommended by spec to ensure ETM writes are committed prior * to resuming execution */ \ mb(); \ etm_write(base, 0x0, CORESIGHT_LAR); \ } while (0) #define ETM_UNLOCK(base) \ do { \ etm_write(base, CORESIGHT_UNLOCK, CORESIGHT_LAR); \ /* ensure unlock and any pending writes are committed prior to * programming ETM registers */ \ mb(); \ } while (0) #define DBG_LOCK(base) \ do { \ /* recommended by spec to ensure ETM writes are committed prior * to resuming execution */ \ mb(); \ dbg_write(base, 0x0, CORESIGHT_LAR); \ } while (0) #define DBG_UNLOCK(base) \ do { \ dbg_write(base, CORESIGHT_UNLOCK, CORESIGHT_LAR); \ /* ensure unlock and any pending writes are committed prior to * programming ETM registers */ \ mb(); \ } while (0) uint32_t msm_jtag_save_cntr[NR_CPUS]; uint32_t msm_jtag_restore_cntr[NR_CPUS]; struct dbg_cpu_ctx { void __iomem *base; uint32_t *state; }; struct dbg_ctx { uint8_t arch; uint8_t nr_wp; uint8_t nr_bp; uint8_t nr_ctx_cmp; struct dbg_cpu_ctx *cpu_ctx[NR_CPUS]; bool save_restore_enabled[NR_CPUS]; }; static struct dbg_ctx dbg; struct etm_cpu_ctx { void __iomem *base; struct device *dev; uint32_t *state; }; struct etm_ctx { uint8_t arch; uint8_t nr_addr_cmp; uint8_t nr_data_cmp; uint8_t nr_cntr; uint8_t nr_ext_inp; uint8_t nr_ext_out; uint8_t nr_ctxid_cmp; struct etm_cpu_ctx *cpu_ctx[NR_CPUS]; bool save_restore_enabled[NR_CPUS]; }; static struct etm_ctx etm; static struct clk *clock[NR_CPUS]; static void etm_set_pwrdwn(struct etm_cpu_ctx *etmdata) { uint32_t etmcr; /* ensure all writes are complete before setting pwrdwn */ mb(); etmcr = etm_read(etmdata, ETMCR); etmcr |= BIT(0); etm_write(etmdata, etmcr, ETMCR); } static void etm_clr_pwrdwn(struct etm_cpu_ctx *etmdata) { uint32_t etmcr; etmcr = etm_read(etmdata, ETMCR); etmcr &= ~BIT(0); etm_write(etmdata, etmcr, ETMCR); /* ensure pwrup completes before subsequent register accesses */ mb(); } static void etm_set_prog(struct etm_cpu_ctx *etmdata) { uint32_t etmcr; int count; etmcr = etm_read(etmdata, ETMCR); etmcr |= BIT(10); etm_write(etmdata, etmcr, ETMCR); for (count = TIMEOUT_US; BVAL(etm_read(etmdata, ETMSR), 1) != 1 && count > 0; count--) udelay(1); WARN(count == 0, "timeout while setting prog bit, ETMSR: %#x\n", etm_read(etmdata, ETMSR)); } static inline void etm_save_state(struct etm_cpu_ctx *etmdata) { int i, j; i = 0; ETM_UNLOCK(etmdata); switch (etm.arch) { case ETM_ARCH_V3_5: etmdata->state[i++] = etm_read(etmdata, ETMTRIGGER); etmdata->state[i++] = etm_read(etmdata, ETMASICCTLR); etmdata->state[i++] = etm_read(etmdata, ETMSR); etmdata->state[i++] = etm_read(etmdata, ETMTSSCR); etmdata->state[i++] = etm_read(etmdata, ETMTECR2); etmdata->state[i++] = etm_read(etmdata, ETMTEEVR); etmdata->state[i++] = etm_read(etmdata, ETMTECR1); etmdata->state[i++] = etm_read(etmdata, ETMFFLR); etmdata->state[i++] = etm_read(etmdata, ETMVDEVR); etmdata->state[i++] = etm_read(etmdata, ETMVDCR1); etmdata->state[i++] = etm_read(etmdata, ETMVDCR3); for (j = 0; j < etm.nr_addr_cmp; j++) { etmdata->state[i++] = etm_read(etmdata, ETMACVRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMACTRn(j)); } for (j = 0; j < etm.nr_data_cmp; j++) { etmdata->state[i++] = etm_read(etmdata, ETMDCVRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMDCMRn(j)); } for (j = 0; j < etm.nr_cntr; j++) { etmdata->state[i++] = etm_read(etmdata, ETMCNTRLDVRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMCNTENRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMCNTRLDEVRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMCNTVRn(j)); } etmdata->state[i++] = etm_read(etmdata, ETMSQ12EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQ21EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQ23EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQ31EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQ32EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQ13EVR); etmdata->state[i++] = etm_read(etmdata, ETMSQR); for (j = 0; j < etm.nr_ext_out; j++) etmdata->state[i++] = etm_read(etmdata, ETMEXTOUTEVRn(j)); for (j = 0; j < etm.nr_ctxid_cmp; j++) etmdata->state[i++] = etm_read(etmdata, ETMCIDCVRn(j)); etmdata->state[i++] = etm_read(etmdata, ETMCIDCMR); etmdata->state[i++] = etm_read(etmdata, ETMSYNCFR); etmdata->state[i++] = etm_read(etmdata, ETMEXTINSELR); etmdata->state[i++] = etm_read(etmdata, ETMTSEVR); etmdata->state[i++] = etm_read(etmdata, ETMAUXCR); etmdata->state[i++] = etm_read(etmdata, ETMTRACEIDR); etmdata->state[i++] = etm_read(etmdata, ETMVMIDCVR); etmdata->state[i++] = etm_read(etmdata, ETMCLAIMCLR); etmdata->state[i++] = etm_read(etmdata, ETMCR); break; default: pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch, __func__); } ETM_LOCK(etmdata); } static inline void etm_restore_state(struct etm_cpu_ctx *etmdata) { int i, j; i = 0; ETM_UNLOCK(etmdata); switch (etm.arch) { case ETM_ARCH_V3_5: etm_clr_pwrdwn(etmdata); etm_write(etmdata, etmdata->state[i++], ETMTRIGGER); etm_write(etmdata, etmdata->state[i++], ETMASICCTLR); etm_write(etmdata, etmdata->state[i++], ETMSR); etm_write(etmdata, etmdata->state[i++], ETMTSSCR); etm_write(etmdata, etmdata->state[i++], ETMTECR2); etm_write(etmdata, etmdata->state[i++], ETMTEEVR); etm_write(etmdata, etmdata->state[i++], ETMTECR1); etm_write(etmdata, etmdata->state[i++], ETMFFLR); etm_write(etmdata, etmdata->state[i++], ETMVDEVR); etm_write(etmdata, etmdata->state[i++], ETMVDCR1); etm_write(etmdata, etmdata->state[i++], ETMVDCR3); for (j = 0; j < etm.nr_addr_cmp; j++) { etm_write(etmdata, etmdata->state[i++], ETMACVRn(j)); etm_write(etmdata, etmdata->state[i++], ETMACTRn(j)); } for (j = 0; j < etm.nr_data_cmp; j++) { etm_write(etmdata, etmdata->state[i++], ETMDCVRn(j)); etm_write(etmdata, etmdata->state[i++], ETMDCMRn(j)); } for (j = 0; j < etm.nr_cntr; j++) { etm_write(etmdata, etmdata->state[i++], ETMCNTRLDVRn(j)); etm_write(etmdata, etmdata->state[i++], ETMCNTENRn(j)); etm_write(etmdata, etmdata->state[i++], ETMCNTRLDEVRn(j)); etm_write(etmdata, etmdata->state[i++], ETMCNTVRn(j)); } etm_write(etmdata, etmdata->state[i++], ETMSQ12EVR); etm_write(etmdata, etmdata->state[i++], ETMSQ21EVR); etm_write(etmdata, etmdata->state[i++], ETMSQ23EVR); etm_write(etmdata, etmdata->state[i++], ETMSQ31EVR); etm_write(etmdata, etmdata->state[i++], ETMSQ32EVR); etm_write(etmdata, etmdata->state[i++], ETMSQ13EVR); etm_write(etmdata, etmdata->state[i++], ETMSQR); for (j = 0; j < etm.nr_ext_out; j++) etm_write(etmdata, etmdata->state[i++], ETMEXTOUTEVRn(j)); for (j = 0; j < etm.nr_ctxid_cmp; j++) etm_write(etmdata, etmdata->state[i++], ETMCIDCVRn(j)); etm_write(etmdata, etmdata->state[i++], ETMCIDCMR); etm_write(etmdata, etmdata->state[i++], ETMSYNCFR); etm_write(etmdata, etmdata->state[i++], ETMEXTINSELR); etm_write(etmdata, etmdata->state[i++], ETMTSEVR); etm_write(etmdata, etmdata->state[i++], ETMAUXCR); etm_write(etmdata, etmdata->state[i++], ETMTRACEIDR); etm_write(etmdata, etmdata->state[i++], ETMVMIDCVR); etm_write(etmdata, etmdata->state[i++], ETMCLAIMSET); /* * Set ETMCR at last as we dont know the saved status of pwrdwn * bit */ etm_write(etmdata, etmdata->state[i++], ETMCR); break; default: pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch, __func__); } ETM_LOCK(etmdata); } static inline void dbg_save_state(struct dbg_cpu_ctx *dbgdata) { int i, j; i = 0; DBG_UNLOCK(dbgdata); dbgdata->state[i++] = dbg_read(dbgdata, DBGWFAR); dbgdata->state[i++] = dbg_read(dbgdata, DBGVCR); for (j = 0; j < dbg.nr_bp; j++) { dbgdata->state[i++] = dbg_read(dbgdata, DBGBVRn(j)); dbgdata->state[i++] = dbg_read(dbgdata, DBGBCRn(j)); } for (j = 0; j < dbg.nr_wp; j++) { dbgdata->state[i++] = dbg_read(dbgdata, DBGWVRn(j)); dbgdata->state[i++] = dbg_read(dbgdata, DBGWCRn(j)); } dbgdata->state[i++] = dbg_read(dbgdata, DBGPRCR); dbgdata->state[i++] = dbg_read(dbgdata, DBGCLAIMSET); dbgdata->state[i++] = dbg_read(dbgdata, DBGCLAIMCLR); dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRTXext); dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRRXext); dbgdata->state[i++] = dbg_read(dbgdata, DBGDSCRext); DBG_LOCK(dbgdata); } static inline void dbg_restore_state(struct dbg_cpu_ctx *dbgdata) { int i, j; i = 0; DBG_UNLOCK(dbgdata); dbg_write(dbgdata, dbgdata->state[i++], DBGWFAR); dbg_write(dbgdata, dbgdata->state[i++], DBGVCR); for (j = 0; j < dbg.nr_bp; j++) { dbg_write(dbgdata, dbgdata->state[i++], DBGBVRn(j)); dbg_write(dbgdata, dbgdata->state[i++], DBGBCRn(j)); } for (j = 0; j < dbg.nr_wp; j++) { dbg_write(dbgdata, dbgdata->state[i++], DBGWVRn(j)); dbg_write(dbgdata, dbgdata->state[i++], DBGWCRn(j)); } dbg_write(dbgdata, dbgdata->state[i++], DBGPRCR); dbg_write(dbgdata, dbgdata->state[i++], DBGCLAIMSET); dbg_write(dbgdata, dbgdata->state[i++], DBGCLAIMCLR); dbg_write(dbgdata, dbgdata->state[i++], DBGDTRTXext); dbg_write(dbgdata, dbgdata->state[i++], DBGDTRRXext); dbg_write(dbgdata, dbgdata->state[i++] & DBGDSCR_MASK, DBGDSCRext); DBG_LOCK(dbgdata); } void msm_jtag_save_state(void) { int cpu; cpu = raw_smp_processor_id(); msm_jtag_save_cntr[cpu]++; /* ensure counter is updated before moving forward */ mb(); if (dbg.save_restore_enabled[cpu]) dbg_save_state(dbg.cpu_ctx[cpu]); if (etm.save_restore_enabled[cpu]) etm_save_state(etm.cpu_ctx[cpu]); } EXPORT_SYMBOL(msm_jtag_save_state); void msm_jtag_restore_state(void) { int cpu; cpu = raw_smp_processor_id(); /* Attempt restore only if save has been done. If power collapse * is disabled, hotplug off of non-boot core will result in WFI * and hence msm_jtag_save_state will not occur. Subsequently, * during hotplug on of non-boot core when msm_jtag_restore_state * is called via msm_platform_secondary_init, this check will help * bail us out without restoring. */ if (msm_jtag_save_cntr[cpu] == msm_jtag_restore_cntr[cpu]) return; else if (msm_jtag_save_cntr[cpu] != msm_jtag_restore_cntr[cpu] + 1) pr_err_ratelimited("jtag imbalance, save:%lu, restore:%lu\n", (unsigned long)msm_jtag_save_cntr[cpu], (unsigned long)msm_jtag_restore_cntr[cpu]); msm_jtag_restore_cntr[cpu]++; /* ensure counter is updated before moving forward */ mb(); if (dbg.save_restore_enabled[cpu]) dbg_restore_state(dbg.cpu_ctx[cpu]); if (etm.save_restore_enabled[cpu]) etm_restore_state(etm.cpu_ctx[cpu]); } EXPORT_SYMBOL(msm_jtag_restore_state); static inline bool etm_arch_supported(uint8_t arch) { switch (arch) { case ETM_ARCH_V3_5: break; default: return false; } return true; } static void __devinit etm_init_arch_data(void *info) { uint32_t etmidr; uint32_t etmccr; struct etm_cpu_ctx *etmdata = info; /* * Clear power down bit since when this bit is set writes to * certain registers might be ignored. */ ETM_UNLOCK(etmdata); etm_clr_pwrdwn(etmdata); /* Set prog bit. It will be set from reset but this is included to * ensure it is set */ etm_set_prog(etmdata); /* find all capabilities */ etmidr = etm_read(etmdata, ETMIDR); etm.arch = BMVAL(etmidr, 4, 11); etmccr = etm_read(etmdata, ETMCCR); etm.nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2; etm.nr_data_cmp = BMVAL(etmccr, 4, 7); etm.nr_cntr = BMVAL(etmccr, 13, 15); etm.nr_ext_inp = BMVAL(etmccr, 17, 19); etm.nr_ext_out = BMVAL(etmccr, 20, 22); etm.nr_ctxid_cmp = BMVAL(etmccr, 24, 25); etm_set_pwrdwn(etmdata); ETM_LOCK(etmdata); } static int __devinit jtag_mm_etm_probe(struct platform_device *pdev, uint32_t cpu) { struct etm_cpu_ctx *etmdata; struct resource *res; struct device *dev = &pdev->dev; /* Allocate memory per cpu */ etmdata = devm_kzalloc(dev, sizeof(struct etm_cpu_ctx), GFP_KERNEL); if (!etmdata) return -ENOMEM; etm.cpu_ctx[cpu] = etmdata; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); etmdata->base = devm_ioremap(dev, res->start, resource_size(res)); if (!etmdata->base) return -EINVAL; /* Allocate etm state save space per core */ etmdata->state = devm_kzalloc(dev, (MAX_ETM_STATE_SIZE * sizeof(uint32_t)), GFP_KERNEL); if (!etmdata->state) return -ENOMEM; smp_call_function_single(0, etm_init_arch_data, etmdata, 1); if (etm_arch_supported(etm.arch)) { if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) etm.save_restore_enabled[cpu] = true; else pr_info("etm save-restore supported by TZ\n"); } else pr_info("etm arch %u not supported\n", etm.arch); return 0; } static inline bool dbg_arch_supported(uint8_t arch) { switch (arch) { case ARM_DEBUG_ARCH_V7B: break; default: return false; } return true; } static void __devinit dbg_init_arch_data(void *info) { uint32_t dbgdidr; struct dbg_cpu_ctx *dbgdata = info; /* This will run on core0 so use it to populate parameters */ dbgdidr = dbg_read(dbgdata, DBGDIDR); dbg.arch = BMVAL(dbgdidr, 16, 19); dbg.nr_ctx_cmp = BMVAL(dbgdidr, 20, 23) + 1; dbg.nr_bp = BMVAL(dbgdidr, 24, 27) + 1; dbg.nr_wp = BMVAL(dbgdidr, 28, 31) + 1; } static int __devinit jtag_mm_dbg_probe(struct platform_device *pdev, uint32_t cpu) { struct dbg_cpu_ctx *dbgdata; struct resource *res; struct device *dev = &pdev->dev; /* Allocate memory per cpu */ dbgdata = devm_kzalloc(dev, sizeof(struct dbg_cpu_ctx), GFP_KERNEL); if (!dbgdata) return -ENOMEM; dbg.cpu_ctx[cpu] = dbgdata; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); dbgdata->base = devm_ioremap(dev, res->start, resource_size(res)); if (!dbgdata->base) return -EINVAL; /* Allocate etm state save space per core */ dbgdata->state = devm_kzalloc(dev, (MAX_DBG_STATE_SIZE * sizeof(uint32_t)), GFP_KERNEL); if (!dbgdata->state) return -ENOMEM; smp_call_function_single(0, dbg_init_arch_data, dbgdata, 1); if (dbg_arch_supported(dbg.arch)) { if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) dbg.save_restore_enabled[cpu] = true; else pr_info("dbg save-restore supported by TZ\n"); } else pr_info("dbg arch %u not supported\n", dbg.arch); return 0; } static int __devinit jtag_mm_probe(struct platform_device *pdev) { int etm_ret, dbg_ret, ret; static uint32_t cpu; static uint32_t count; struct device *dev = &pdev->dev; cpu = count; count++; clock[cpu] = devm_clk_get(dev, "core_clk"); if (IS_ERR(clock[cpu])) { ret = PTR_ERR(clock[cpu]); return ret; } ret = clk_set_rate(clock[cpu], CORESIGHT_CLK_RATE_TRACE); if (ret) return ret; ret = clk_prepare_enable(clock[cpu]); if (ret) return ret; platform_set_drvdata(pdev, clock[cpu]); etm_ret = jtag_mm_etm_probe(pdev, cpu); dbg_ret = jtag_mm_dbg_probe(pdev, cpu); /* The probe succeeds even when only one of the etm and dbg probes * succeeds. This allows us to save-restore etm and dbg registers * independently. */ if (etm_ret && dbg_ret) { clk_disable_unprepare(clock[cpu]); ret = etm_ret; } else ret = 0; return ret; } static int __devexit jtag_mm_remove(struct platform_device *pdev) { struct clk *clock = platform_get_drvdata(pdev); clk_disable_unprepare(clock); return 0; } static struct of_device_id msm_qdss_mm_match[] = { { .compatible = "qcom,jtag-mm"}, {} }; static struct platform_driver jtag_mm_driver = { .probe = jtag_mm_probe, .remove = __devexit_p(jtag_mm_remove), .driver = { .name = "msm-jtag-mm", .owner = THIS_MODULE, .of_match_table = msm_qdss_mm_match, }, }; static int __init jtag_mm_init(void) { return platform_driver_register(&jtag_mm_driver); } module_init(jtag_mm_init); static void __exit jtag_mm_exit(void) { platform_driver_unregister(&jtag_mm_driver); } module_exit(jtag_mm_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Coresight debug and ETM save-restore driver");