Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 474f1a66 authored by Sonic Zhang's avatar Sonic Zhang Committed by Bryan Wu
Browse files

Blackfin arch: kgdb specific code

parent 1c5d2265
Loading
Loading
Loading
Loading
+155 −0
Original line number Diff line number Diff line
			A Simple Guide to Configure KGDB

			Sonic Zhang <sonic.zhang@analog.com>
				Aug. 24th 2006


This KGDB patch enables the kernel developer to do source level debugging on
the kernel for the Blackfin architecture.  The debugging works over either the
ethernet interface or one of the uarts.  Both software breakpoints and
hardware breakpoints are supported in this version.
http://docs.blackfin.uclinux.org/doku.php?id=kgdb


2 known issues:
1. This bug:
       http://blackfin.uclinux.org/tracker/index.php?func=detail&aid=544&group_id=18&atid=145
   The GDB client for Blackfin uClinux causes incorrect values of local
   variables to be displayed when the user breaks the running of kernel in GDB.
2. Because of a hardware bug in Blackfin 533 v1.0.3:
       05000067 - Watchpoints (Hardware Breakpoints) are not supported
   Hardware breakpoints cannot be set properly.


Debug over Ethernet:
 
1. Compile and install the cross platform version of gdb for blackfin, which
   can be found at $(BINROOT)/bfin-elf-gdb.

2. Apply this patch to the 2.6.x kernel.  Select the menuconfig option under
   "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
   With this selected, option "Full Symbolic/Source Debugging support" and 
   "Compile the kernel with frame pointers" are also selected.

3. Select option "KGDB: connect over (Ethernet)".  Add "kgdboe=@target-IP/,@host-IP/" to
   the option "Compiled-in Kernel Boot Parameter" under "Kernel hacking".

4. Connect minicom to the serial port and boot the kernel image.

5. Configure the IP "/> ifconfig eth0 target-IP"

6. Start GDB client "bfin-elf-gdb vmlinux".

7. Connect to the target "(gdb) target remote udp:target-IP:6443".

8. Set software breakpoint "(gdb) break sys_open".

9. Continue "(gdb) c".

10. Run ls in the target console "/> ls".

11. Breakpoint hits. "Breakpoint 1: sys_open(..."

12. Display local variables and function paramters.
    (*) This operation gives wrong results, see known issue 1.

13. Single stepping "(gdb) si".

14. Remove breakpoint 1. "(gdb) del 1"

15. Set hardware breakpoint "(gdb) hbreak sys_open".

16. Continue "(gdb) c".

17. Run ls in the target console "/> ls".

18. Hardware breakpoint hits. "Breakpoint 1: sys_open(...".
    (*) This hardware breakpoint will not be hit, see known issue 2.

19. Continue "(gdb) c".

20. Interrupt the target in GDB "Ctrl+C".

21. Detach from the target "(gdb) detach".

22. Exit GDB "(gdb) quit".


Debug over the UART:

1. Compile and install the cross platform version of gdb for blackfin, which
   can be found at $(BINROOT)/bfin-elf-gdb.

2. Apply this patch to the 2.6.x kernel.  Select the menuconfig option under
   "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
   With this selected, option "Full Symbolic/Source Debugging support" and 
   "Compile the kernel with frame pointers" are also selected.

3. Select option "KGDB: connect over (UART)".  Set "KGDB: UART port number" to be
   a different one from the console.  Don't forget to change the mode of
   blackfin serial driver to PIO.  Otherwise kgdb works incorrectly on UART.
 
4. If you want connect to kgdb when the kernel boots, enable
   "KGDB: Wait for gdb connection early" 

5. Compile kernel.

6. Connect minicom to the serial port of the console and boot the kernel image.

7. Start GDB client "bfin-elf-gdb vmlinux".

8. Set the baud rate in GDB "(gdb) set remotebaud 57600".

9. Connect to the target on the second serial port "(gdb) target remote /dev/ttyS1".

10. Set software breakpoint "(gdb) break sys_open".

11. Continue "(gdb) c". 

12. Run ls in the target console "/> ls". 

13. A breakpoint is hit. "Breakpoint 1: sys_open(..."

14. All other operations are the same as that in KGDB over Ethernet. 


Debug over the same UART as console:

1. Compile and install the cross platform version of gdb for blackfin, which
   can be found at $(BINROOT)/bfin-elf-gdb.

2. Apply this patch to the 2.6.x kernel.  Select the menuconfig option under
   "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
   With this selected, option "Full Symbolic/Source Debugging support" and 
   "Compile the kernel with frame pointers" are also selected.

3. Select option "KGDB: connect over UART".  Set "KGDB: UART port number" to console.
   Don't forget to change the mode of blackfin serial driver to PIO.
   Otherwise kgdb works incorrectly on UART.
 
4. If you want connect to kgdb when the kernel boots, enable
   "KGDB: Wait for gdb connection early" 

5. Connect minicom to the serial port and boot the kernel image. 

6. (Optional) Ask target to wait for gdb connection by entering Ctrl+A. In minicom, you should enter Ctrl+A+A.

7. Start GDB client "bfin-elf-gdb vmlinux".

8. Set the baud rate in GDB "(gdb) set remotebaud 57600".

9. Connect to the target "(gdb) target remote /dev/ttyS0".

10. Set software breakpoint "(gdb) break sys_open".

11. Continue "(gdb) c". Then enter Ctrl+C twice to stop GDB connection.

12. Run ls in the target console "/> ls". Dummy string can be seen on the console.

13. Then connect the gdb to target again. "(gdb) target remote /dev/ttyS0".
    Now you will find a breakpoint is hit. "Breakpoint 1: sys_open(..."

14. All other operations are the same as that in KGDB over Ethernet.  The only
    difference is that after continue command in GDB, please stop GDB
    connection by 2 "Ctrl+C"s and connect again after breakpoints are hit or
    Ctrl+A is entered.
+1 −0
Original line number Diff line number Diff line
@@ -14,3 +14,4 @@ obj-$(CONFIG_BF561) += bfin_gpio.o
obj-$(CONFIG_MODULES)                += module.o
obj-$(CONFIG_BFIN_DMA_5XX)           += bfin_dma_5xx.o
obj-$(CONFIG_DUAL_CORE_TEST_MODULE)  += dualcore_test.o
obj-$(CONFIG_KGDB)                   += kgdb.o
+421 −0
Original line number Diff line number Diff line
/*
 * File:         arch/blackfin/kernel/kgdb.c
 * Based on:
 * Author:       Sonic Zhang
 *
 * Created:
 * Description:
 *
 * Rev:          $Id: kgdb_bfin_linux-2.6.x.patch 4934 2007-02-13 09:32:11Z sonicz $
 *
 * Modified:
 *               Copyright 2005-2006 Analog Devices Inc.
 *
 * Bugs:         Enter bugs at http://blackfin.uclinux.org/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see the file COPYING, or write
 * to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/ptrace.h>		/* for linux pt_regs struct */
#include <linux/kgdb.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/debugger.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <asm/system.h>
#include <asm/traps.h>
#include <asm/blackfin.h>

/* Put the error code here just in case the user cares.  */
int gdb_bf533errcode;
/* Likewise, the vector number here (since GDB only gets the signal
   number through the usual means, and that's not very specific).  */
int gdb_bf533vector = -1;

#if KGDB_MAX_NO_CPUS != 8
#error change the definition of slavecpulocks
#endif

void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
	gdb_regs[BFIN_R0] = regs->r0;
	gdb_regs[BFIN_R1] = regs->r1;
	gdb_regs[BFIN_R2] = regs->r2;
	gdb_regs[BFIN_R3] = regs->r3;
	gdb_regs[BFIN_R4] = regs->r4;
	gdb_regs[BFIN_R5] = regs->r5;
	gdb_regs[BFIN_R6] = regs->r6;
	gdb_regs[BFIN_R7] = regs->r7;
	gdb_regs[BFIN_P0] = regs->p0;
	gdb_regs[BFIN_P1] = regs->p1;
	gdb_regs[BFIN_P2] = regs->p2;
	gdb_regs[BFIN_P3] = regs->p3;
	gdb_regs[BFIN_P4] = regs->p4;
	gdb_regs[BFIN_P5] = regs->p5;
	gdb_regs[BFIN_SP] = regs->reserved;
	gdb_regs[BFIN_FP] = regs->fp;
	gdb_regs[BFIN_I0] = regs->i0;
	gdb_regs[BFIN_I1] = regs->i1;
	gdb_regs[BFIN_I2] = regs->i2;
	gdb_regs[BFIN_I3] = regs->i3;
	gdb_regs[BFIN_M0] = regs->m0;
	gdb_regs[BFIN_M1] = regs->m1;
	gdb_regs[BFIN_M2] = regs->m2;
	gdb_regs[BFIN_M3] = regs->m3;
	gdb_regs[BFIN_B0] = regs->b0;
	gdb_regs[BFIN_B1] = regs->b1;
	gdb_regs[BFIN_B2] = regs->b2;
	gdb_regs[BFIN_B3] = regs->b3;
	gdb_regs[BFIN_L0] = regs->l0;
	gdb_regs[BFIN_L1] = regs->l1;
	gdb_regs[BFIN_L2] = regs->l2;
	gdb_regs[BFIN_L3] = regs->l3;
	gdb_regs[BFIN_A0_DOT_X] = regs->a0x;
	gdb_regs[BFIN_A0_DOT_W] = regs->a0w;
	gdb_regs[BFIN_A1_DOT_X] = regs->a1x;
	gdb_regs[BFIN_A1_DOT_W] = regs->a1w;
	gdb_regs[BFIN_ASTAT] = regs->astat;
	gdb_regs[BFIN_RETS] = regs->rets;
	gdb_regs[BFIN_LC0] = regs->lc0;
	gdb_regs[BFIN_LT0] = regs->lt0;
	gdb_regs[BFIN_LB0] = regs->lb0;
	gdb_regs[BFIN_LC1] = regs->lc1;
	gdb_regs[BFIN_LT1] = regs->lt1;
	gdb_regs[BFIN_LB1] = regs->lb1;
	gdb_regs[BFIN_CYCLES] = 0;
	gdb_regs[BFIN_CYCLES2] = 0;
	gdb_regs[BFIN_USP] = regs->usp;
	gdb_regs[BFIN_SEQSTAT] = regs->seqstat;
	gdb_regs[BFIN_SYSCFG] = regs->syscfg;
	gdb_regs[BFIN_RETI] = regs->pc;
	gdb_regs[BFIN_RETX] = regs->retx;
	gdb_regs[BFIN_RETN] = regs->retn;
	gdb_regs[BFIN_RETE] = regs->rete;
	gdb_regs[BFIN_PC] = regs->pc;
	gdb_regs[BFIN_CC] = 0;
	gdb_regs[BFIN_EXTRA1] = 0;
	gdb_regs[BFIN_EXTRA2] = 0;
	gdb_regs[BFIN_EXTRA3] = 0;
	gdb_regs[BFIN_IPEND] = regs->ipend;
}

/*
 * Extracts ebp, esp and eip values understandable by gdb from the values
 * saved by switch_to.
 * thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp
 * prior to entering switch_to is 8 greater then the value that is saved.
 * If switch_to changes, change following code appropriately.
 */
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
{
	gdb_regs[BFIN_SP] = p->thread.ksp;
	gdb_regs[BFIN_PC] = p->thread.pc;
	gdb_regs[BFIN_SEQSTAT] = p->thread.seqstat;
}

void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
	regs->r0 = gdb_regs[BFIN_R0];
	regs->r1 = gdb_regs[BFIN_R1];
	regs->r2 = gdb_regs[BFIN_R2];
	regs->r3 = gdb_regs[BFIN_R3];
	regs->r4 = gdb_regs[BFIN_R4];
	regs->r5 = gdb_regs[BFIN_R5];
	regs->r6 = gdb_regs[BFIN_R6];
	regs->r7 = gdb_regs[BFIN_R7];
	regs->p0 = gdb_regs[BFIN_P0];
	regs->p1 = gdb_regs[BFIN_P1];
	regs->p2 = gdb_regs[BFIN_P2];
	regs->p3 = gdb_regs[BFIN_P3];
	regs->p4 = gdb_regs[BFIN_P4];
	regs->p5 = gdb_regs[BFIN_P5];
	regs->fp = gdb_regs[BFIN_FP];
	regs->i0 = gdb_regs[BFIN_I0];
	regs->i1 = gdb_regs[BFIN_I1];
	regs->i2 = gdb_regs[BFIN_I2];
	regs->i3 = gdb_regs[BFIN_I3];
	regs->m0 = gdb_regs[BFIN_M0];
	regs->m1 = gdb_regs[BFIN_M1];
	regs->m2 = gdb_regs[BFIN_M2];
	regs->m3 = gdb_regs[BFIN_M3];
	regs->b0 = gdb_regs[BFIN_B0];
	regs->b1 = gdb_regs[BFIN_B1];
	regs->b2 = gdb_regs[BFIN_B2];
	regs->b3 = gdb_regs[BFIN_B3];
	regs->l0 = gdb_regs[BFIN_L0];
	regs->l1 = gdb_regs[BFIN_L1];
	regs->l2 = gdb_regs[BFIN_L2];
	regs->l3 = gdb_regs[BFIN_L3];
	regs->a0x = gdb_regs[BFIN_A0_DOT_X];
	regs->a0w = gdb_regs[BFIN_A0_DOT_W];
	regs->a1x = gdb_regs[BFIN_A1_DOT_X];
	regs->a1w = gdb_regs[BFIN_A1_DOT_W];
	regs->rets = gdb_regs[BFIN_RETS];
	regs->lc0 = gdb_regs[BFIN_LC0];
	regs->lt0 = gdb_regs[BFIN_LT0];
	regs->lb0 = gdb_regs[BFIN_LB0];
	regs->lc1 = gdb_regs[BFIN_LC1];
	regs->lt1 = gdb_regs[BFIN_LT1];
	regs->lb1 = gdb_regs[BFIN_LB1];
	regs->usp = gdb_regs[BFIN_USP];
	regs->syscfg = gdb_regs[BFIN_SYSCFG];
	regs->retx = gdb_regs[BFIN_PC];
	regs->retn = gdb_regs[BFIN_RETN];
	regs->rete = gdb_regs[BFIN_RETE];
	regs->pc = gdb_regs[BFIN_PC];

#if 0				/* can't change these */
	regs->astat = gdb_regs[BFIN_ASTAT];
	regs->seqstat = gdb_regs[BFIN_SEQSTAT];
	regs->ipend = gdb_regs[BFIN_IPEND];
#endif
}

struct hw_breakpoint {
	unsigned int occupied:1;
	unsigned int skip:1;
	unsigned int enabled:1;
	unsigned int type:1;
	unsigned int dataacc:2;
	unsigned short count;
	unsigned int addr;
} breakinfo[HW_BREAKPOINT_NUM];

int kgdb_arch_init(void)
{
	kgdb_remove_all_hw_break();
	return 0;
}

int kgdb_set_hw_break(unsigned long addr)
{
	int breakno;
	for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
		if (!breakinfo[breakno].occupied) {
			breakinfo[breakno].occupied = 1;
			breakinfo[breakno].enabled = 1;
			breakinfo[breakno].type = 1;
			breakinfo[breakno].addr = addr;
			return 0;
		}

	return -ENOSPC;
}

int kgdb_remove_hw_break(unsigned long addr)
{
	int breakno;
	for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
		if (breakinfo[breakno].addr == addr)
			memset(&(breakinfo[breakno]), 0, sizeof(struct hw_breakpoint));

	return 0;
}

void kgdb_remove_all_hw_break(void)
{
	memset(breakinfo, 0, sizeof(struct hw_breakpoint)*8);
}

/*
void kgdb_show_info(void)
{
	printk(KERN_DEBUG "hwd: wpia0=0x%x, wpiacnt0=%d, wpiactl=0x%x, wpstat=0x%x\n",
		bfin_read_WPIA0(), bfin_read_WPIACNT0(),
		bfin_read_WPIACTL(), bfin_read_WPSTAT());
}
*/

void kgdb_correct_hw_break(void)
{
	int breakno;
	int correctit;
	uint32_t wpdactl = bfin_read_WPDACTL();

	correctit = 0;
	for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) {
		if (breakinfo[breakno].type == 1) {
			switch (breakno) {
			case 0:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN0)) {
					correctit = 1;
					wpdactl &= ~(WPIREN01|EMUSW0);
					wpdactl |= WPIAEN0|WPICNTEN0;
					bfin_write_WPIA0(breakinfo[breakno].addr);
					bfin_write_WPIACNT0(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN0)) {
					correctit = 1;
					wpdactl &= ~WPIAEN0;
				}
				break;

			case 1:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN1)) {
					correctit = 1;
					wpdactl &= ~(WPIREN01|EMUSW1);
					wpdactl |= WPIAEN1|WPICNTEN1;
					bfin_write_WPIA1(breakinfo[breakno].addr);
					bfin_write_WPIACNT1(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN1)) {
					correctit = 1;
					wpdactl &= ~WPIAEN1;
				}
				break;

			case 2:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN2)) {
					correctit = 1;
					wpdactl &= ~(WPIREN23|EMUSW2);
					wpdactl |= WPIAEN2|WPICNTEN2;
					bfin_write_WPIA2(breakinfo[breakno].addr);
					bfin_write_WPIACNT2(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN2)) {
					correctit = 1;
					wpdactl &= ~WPIAEN2;
				}
				break;

			case 3:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN3)) {
					correctit = 1;
					wpdactl &= ~(WPIREN23|EMUSW3);
					wpdactl |= WPIAEN3|WPICNTEN3;
					bfin_write_WPIA3(breakinfo[breakno].addr);
					bfin_write_WPIACNT3(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN3)) {
					correctit = 1;
					wpdactl &= ~WPIAEN3;
				}
				break;
			case 4:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN4)) {
					correctit = 1;
					wpdactl &= ~(WPIREN45|EMUSW4);
					wpdactl |= WPIAEN4|WPICNTEN4;
					bfin_write_WPIA4(breakinfo[breakno].addr);
					bfin_write_WPIACNT4(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN4)) {
					correctit = 1;
					wpdactl &= ~WPIAEN4;
				}
				break;
			case 5:
				if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN5)) {
					correctit = 1;
					wpdactl &= ~(WPIREN45|EMUSW5);
					wpdactl |= WPIAEN5|WPICNTEN5;
					bfin_write_WPIA5(breakinfo[breakno].addr);
					bfin_write_WPIACNT5(breakinfo[breakno].skip);
				} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN5)) {
					correctit = 1;
					wpdactl &= ~WPIAEN5;
				}
				break;
			}
		}
	}
	if (correctit) {
		wpdactl &= ~WPAND;
		wpdactl |= WPPWR;
		/*printk("correct_hw_break: wpdactl=0x%x\n", wpdactl);*/
		bfin_write_WPDACTL(wpdactl);
		CSYNC();
		/*kgdb_show_info();*/
	}
}

void kgdb_disable_hw_debug(struct pt_regs *regs)
{
	/* Disable hardware debugging while we are in kgdb */
	bfin_write_WPIACTL(bfin_read_WPIACTL() & ~0x1);
	CSYNC();
}

void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
{
	/* Master processor is completely in the debugger */
	gdb_bf533vector = eVector;
	gdb_bf533errcode = err_code;
}

int kgdb_arch_handle_exception(int exceptionVector, int signo,
			       int err_code, char *remcom_in_buffer,
			       char *remcom_out_buffer,
			       struct pt_regs *linux_regs)
{
	long addr;
	long breakno;
	char *ptr;
	int newPC;
	int wp_status;

	switch (remcom_in_buffer[0]) {
	case 'c':
	case 's':
		if (kgdb_contthread && kgdb_contthread != current) {
			strcpy(remcom_out_buffer, "E00");
			break;
		}

		kgdb_contthread = NULL;

		/* try to read optional parameter, pc unchanged if no parm */
		ptr = &remcom_in_buffer[1];
		if (kgdb_hex2long(&ptr, &addr)) {
			linux_regs->retx = addr;
		}
		newPC = linux_regs->retx;

		/* clear the trace bit */
		linux_regs->syscfg &= 0xfffffffe;

		/* set the trace bit if we're stepping */
		if (remcom_in_buffer[0] == 's') {
			linux_regs->syscfg |= 0x1;
			debugger_step = 1;
		}

		wp_status = bfin_read_WPSTAT();
		CSYNC();

		if (exceptionVector == VEC_WATCH) {
			for (breakno = 0; breakno < 6; ++breakno) {
				if (wp_status & (1 << breakno)) {
					breakinfo->skip = 1;
					break;
				}
			}
		}
		kgdb_correct_hw_break();

		bfin_write_WPSTAT(0);

		return 0;
	}			/* switch */
	return -1;		/* this means that we do not want to exit from the handler */
}

struct kgdb_arch arch_kgdb_ops = {
	.gdb_bpt_instr = {0xa1},
	.flags = KGDB_HW_BREAKPOINT,
};
+1 −1
Original line number Diff line number Diff line
@@ -556,7 +556,7 @@ choice

config SERIAL_BFIN_DMA
	bool "DMA mode"
	depends on DMA_UNCACHED_1M
	depends on DMA_UNCACHED_1M && !KGDB_UART
	help
	  This driver works under DMA mode. If this option is selected, the
	  blackfin simple dma driver is also enabled.
+105 −0
Original line number Diff line number Diff line
@@ -41,6 +41,11 @@
#include <linux/tty_flip.h>
#include <linux/serial_core.h>

#ifdef CONFIG_KGDB_UART
#include <linux/kgdb.h>
#include <asm/irq_regs.h>
#endif

#include <asm/gpio.h>
#include <asm/mach/bfin_serial_5xx.h>

@@ -119,6 +124,9 @@ static void bfin_serial_stop_rx(struct uart_port *port)
	unsigned short ier;

	ier = UART_GET_IER(uart);
#ifdef	CONFIG_KGDB_UART
	if (uart->port.line != CONFIG_KGDB_UART_PORT)
#endif
	ier &= ~ERBFI;
	UART_PUT_IER(uart, ier);
}
@@ -130,6 +138,49 @@ static void bfin_serial_enable_ms(struct uart_port *port)
{
}

#ifdef CONFIG_KGDB_UART
static int kgdb_entry_state;

void kgdb_put_debug_char(int chr)
{
	struct bfin_serial_port *uart;
	
	if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
		uart = &bfin_serial_ports[0];
	else
		uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
	
	while (!(UART_GET_LSR(uart) & THRE)) {
		__builtin_bfin_ssync();
	}
	UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
	__builtin_bfin_ssync();
	UART_PUT_CHAR(uart, (unsigned char)chr);
	__builtin_bfin_ssync();
}

int kgdb_get_debug_char(void)
{
	struct bfin_serial_port *uart;
	unsigned char chr;

	if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
		uart = &bfin_serial_ports[0];
	else
		uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
	
	while(!(UART_GET_LSR(uart) & DR)) {
		__builtin_bfin_ssync();
	}
	UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
	__builtin_bfin_ssync();
	chr = UART_GET_CHAR(uart);
	__builtin_bfin_ssync();

	return chr;
}
#endif

#ifdef CONFIG_SERIAL_BFIN_PIO
static void local_put_char(struct bfin_serial_port *uart, char ch)
{
@@ -152,6 +203,9 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
{
	struct tty_struct *tty = uart->port.info->tty;
	unsigned int status, ch, flg;
#ifdef CONFIG_KGDB_UART
	struct pt_regs *regs = get_irq_regs();
#endif
#ifdef BF533_FAMILY
	static int in_break = 0;
#endif
@@ -160,6 +214,27 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
 	ch = UART_GET_CHAR(uart);
 	uart->port.icount.rx++;

#ifdef CONFIG_KGDB_UART
	if (uart->port.line == CONFIG_KGDB_UART_PORT) {
		if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */
			kgdb_breakkey_pressed(regs);
			return;
		} else if (kgdb_entry_state == 0 && ch == '$') {/* connection from KGDB */
			kgdb_entry_state = 1;
		} else if (kgdb_entry_state == 1 && ch == 'q') {
			kgdb_entry_state = 0;
			kgdb_breakkey_pressed(regs);
			return;
		} else if (ch == 0x3) {/* Ctrl + C */
			kgdb_entry_state = 0;
			kgdb_breakkey_pressed(regs);
			return;
		} else {
			kgdb_entry_state = 0;
		}
	}
#endif
 
#ifdef BF533_FAMILY
	/* The BF533 family of processors have a nice misbehavior where
	 * they continuously generate characters for a "single" break.
@@ -570,8 +645,12 @@ static int bfin_serial_startup(struct uart_port *port)
	uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
	uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
	add_timer(&(uart->rx_dma_timer));
#else
# ifdef	CONFIG_KGDB_UART
	if (uart->port.line != CONFIG_KGDB_UART_PORT && request_irq
# else
	if (request_irq
# endif
	    (uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED,
	     "BFIN_UART_RX", uart)) {
		printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
@@ -601,6 +680,9 @@ static void bfin_serial_shutdown(struct uart_port *port)
	free_dma(uart->rx_dma_channel);
	del_timer(&(uart->rx_dma_timer));
#else
#ifdef	CONFIG_KGDB_UART
	if (uart->port.line != CONFIG_KGDB_UART_PORT)
#endif
	free_irq(uart->port.irq, uart);
	free_irq(uart->port.irq+1, uart);
#endif
@@ -931,6 +1013,10 @@ static int __init bfin_serial_rs_console_init(void)
{
	bfin_serial_init_ports();
	register_console(&bfin_serial_console);
#ifdef CONFIG_KGDB_UART
	kgdb_entry_state = 0;
	init_kgdb_uart();
#endif
	return 0;
}
console_initcall(bfin_serial_rs_console_init);
@@ -1023,6 +1109,10 @@ static struct platform_driver bfin_serial_driver = {
static int __init bfin_serial_init(void)
{
	int ret;
#ifdef CONFIG_KGDB_UART
	struct bfin_serial_port *uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
	struct termios t;
#endif

	pr_info("Serial: Blackfin serial driver\n");

@@ -1036,6 +1126,21 @@ static int __init bfin_serial_init(void)
			uart_unregister_driver(&bfin_serial_reg);
		}
	}
#ifdef CONFIG_KGDB_UART
	if (uart->port.cons->index != CONFIG_KGDB_UART_PORT) {
		request_irq(uart->port.irq, bfin_serial_int,
			IRQF_DISABLED, "BFIN_UART_RX", uart);
		pr_info("Request irq for kgdb uart port\n");
		UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
		__builtin_bfin_ssync();
		t.c_cflag = CS8|B57600;
		t.c_iflag = 0;
		t.c_oflag = 0;
		t.c_lflag = ICANON;
		t.c_line = CONFIG_KGDB_UART_PORT;
		bfin_serial_set_termios(&uart->port, &t, &t);
	}
#endif
	return ret;
}

Loading