USB: Dump usb info during memory access violation
USB is accessing an invalid memory which results in EBI error.
To find the endpoint information and the address being accessed,
dump all the usb request info required for debugging memory access
violation issue from APP's proc using the trace framework.
CRs-Fixed: 430455
Change-Id: Ie99c31f43dfa9858a9ec2756903574888629e341
Signed-off-by: Chiranjeevi Velempati <cvelempa@codeaurora.org>
Signed-off-by: Rajkumar Raghupathy <raghup@codeaurora.org>
diff --git a/arch/arm/mach-msm/ebi_erp.c b/arch/arm/mach-msm/ebi_erp.c
index eb38101..6b300d8 100644
--- a/arch/arm/mach-msm/ebi_erp.c
+++ b/arch/arm/mach-msm/ebi_erp.c
@@ -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
@@ -18,6 +18,9 @@
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/cpu.h>
+#include <mach/usb_trace.h>
+
+DEFINE_TRACE(usb_daytona_invalid_access);
#define MODULE_NAME "msm_ebi_erp"
@@ -113,6 +116,11 @@
err_cntl |= CNTL_CLEAR_ERR;
writel_relaxed(err_cntl, base + SLV_ERR_CNTL);
mb(); /* Ensure interrupt is cleared before returning */
+
+ if ((err_apacket0 & AMID_MASK) == 0x00000102)
+ trace_usb_daytona_invalid_access(err_addr, err_apacket0,
+ err_apacket1);
+
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-msm/include/mach/usb_trace.h b/arch/arm/mach-msm/include/mach/usb_trace.h
new file mode 100644
index 0000000..02ca8ca
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_trace.h
@@ -0,0 +1,27 @@
+/* include/asm-arm/arch-msm/usbtrace.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef _USB_TRACE_H_
+#define _USB_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+DECLARE_TRACE(usb_daytona_invalid_access,
+ TP_PROTO(unsigned int ebi_addr,
+ unsigned int ebi_apacket0, unsigned int ebi_apacket1),
+ TP_ARGS(ebi_addr, ebi_apacket0, ebi_apacket1));
+
+#endif
+
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 4764973..21ea26f 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -66,7 +66,8 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/msm_hsusb.h>
-
+#include <linux/tracepoint.h>
+#include <mach/usb_trace.h>
#include "ci13xxx_udc.h"
/* Turns on streaming. overrides CI13XXX_DISABLE_STREAMING */
@@ -139,6 +140,21 @@
return n ? n-1 : 32;
}
+struct ci13xxx_ebi_err_entry {
+ u32 *usb_req_buf;
+ u32 usb_req_length;
+ u32 ep_info;
+ struct ci13xxx_ebi_err_entry *next;
+};
+
+struct ci13xxx_ebi_err_data {
+ u32 ebi_err_addr;
+ u32 apkt0;
+ u32 apkt1;
+ struct ci13xxx_ebi_err_entry *ebi_err_entry;
+};
+static struct ci13xxx_ebi_err_data *ebi_err_data;
+
/******************************************************************************
* HW block
*****************************************************************************/
@@ -1744,6 +1760,72 @@
return 0;
}
+static void dump_usb_info(void *ignore, unsigned int ebi_addr,
+ unsigned int ebi_apacket0, unsigned int ebi_apacket1)
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long flags;
+ struct list_head *ptr = NULL;
+ struct ci13xxx_req *req = NULL;
+ struct ci13xxx_ep *mEp;
+ unsigned i;
+ struct ci13xxx_ebi_err_entry *temp_dump;
+ static int count;
+ u32 epdir = 0;
+
+ if (count)
+ return;
+ count++;
+
+ pr_info("%s: USB EBI error detected\n", __func__);
+
+ ebi_err_data = kmalloc(sizeof(struct ci13xxx_ebi_err_data),
+ GFP_ATOMIC);
+ if (!ebi_err_data) {
+ pr_err("%s: memory alloc failed for ebi_err_data\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_entry = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!ebi_err_data->ebi_err_entry) {
+ kfree(ebi_err_data);
+ pr_err("%s: memory alloc failed for ebi_err_entry\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_addr = ebi_addr;
+ ebi_err_data->apkt0 = ebi_apacket0;
+ ebi_err_data->apkt1 = ebi_apacket1;
+
+ temp_dump = ebi_err_data->ebi_err_entry;
+ pr_info("\n DUMPING USB Requests Information\n");
+ spin_lock_irqsave(udc->lock, flags);
+ for (i = 0; i < hw_ep_max; i++) {
+ list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) {
+ mEp = &udc->ci13xxx_ep[i];
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+
+ temp_dump->usb_req_buf = req->req.buf;
+ temp_dump->usb_req_length = req->req.length;
+ epdir = mEp->dir;
+ temp_dump->ep_info = mEp->num | (epdir << 15);
+
+ temp_dump->next = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!temp_dump->next) {
+ pr_err("%s: memory alloc failed\n", __func__);
+ spin_unlock_irqrestore(udc->lock, flags);
+ return;
+ }
+ temp_dump = temp_dump->next;
+ }
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+}
+
/******************************************************************************
* UTIL block
*****************************************************************************/
@@ -3685,6 +3767,11 @@
pm_runtime_no_callbacks(&udc->gadget.dev);
pm_runtime_enable(&udc->gadget.dev);
+ retval = register_trace_usb_daytona_invalid_access(dump_usb_info,
+ NULL);
+ if (retval)
+ pr_err("Registering trace failed\n");
+
_udc = udc;
return retval;
@@ -3718,11 +3805,17 @@
static void udc_remove(void)
{
struct ci13xxx *udc = _udc;
+ int retval;
if (udc == NULL) {
err("EINVAL");
return;
}
+ retval = unregister_trace_usb_daytona_invalid_access(dump_usb_info,
+ NULL);
+ if (retval)
+ pr_err("Unregistering trace failed\n");
+
usb_del_gadget_udc(&udc->gadget);
if (udc->transceiver) {