diff options
Diffstat (limited to 'arch/powerpc/cpu/ppc4xx/bedbug_405.c')
| -rw-r--r-- | arch/powerpc/cpu/ppc4xx/bedbug_405.c | 308 | 
1 files changed, 308 insertions, 0 deletions
| diff --git a/arch/powerpc/cpu/ppc4xx/bedbug_405.c b/arch/powerpc/cpu/ppc4xx/bedbug_405.c new file mode 100644 index 000000000..ef11cb65e --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/bedbug_405.c @@ -0,0 +1,308 @@ +/* + * Bedbug Functions specific to the PPC405 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if defined(CONFIG_CMD_BEDBUG) && defined(CONFIG_4xx) + +#define MAX_BREAK_POINTS 4 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug405_init __P ((void)); +void bedbug405_do_break __P ((cmd_tbl_t *, int, int, char *[])); +void bedbug405_break_isr __P ((struct pt_regs *)); +int bedbug405_find_empty __P ((void)); +int bedbug405_set __P ((int, unsigned long)); +int bedbug405_clear __P ((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the AMCC PPC405.	Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug405_init (void) +{ +	int i; + +	/* -------------------------------------------------- */ + +	bug_ctx.hw_debug_enabled = 0; +	bug_ctx.stopped = 0; +	bug_ctx.current_bp = 0; +	bug_ctx.regs = NULL; + +	bug_ctx.do_break = bedbug405_do_break; +	bug_ctx.break_isr = bedbug405_break_isr; +	bug_ctx.find_empty = bedbug405_find_empty; +	bug_ctx.set = bedbug405_set; +	bug_ctx.clear = bedbug405_clear; + +	for (i = 1; i <= MAX_BREAK_POINTS; ++i) +		(*bug_ctx.clear) (i); + +	puts ("BEDBUG:ready\n"); +	return; +}	/* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 405.	The "off" + * string will disable a specific breakpoint.  The "show" string will + * display the current breakpoints.  Otherwise an address will set a + * breakpoint at that address.	Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug405_do_break (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	long addr = 0;		/* Address to break at  */ +	int which_bp;		/* Breakpoint number    */ + +	/* -------------------------------------------------- */ + +	if (argc < 2) { +		cmd_usage(cmdtp); +		return; +	} + +	/* Turn off a breakpoint */ + +	if (strcmp (argv[1], "off") == 0) { +		if (bug_ctx.hw_debug_enabled == 0) { +			printf ("No breakpoints enabled\n"); +			return; +		} + +		which_bp = simple_strtoul (argv[2], NULL, 10); + +		if (bug_ctx.clear) +			(*bug_ctx.clear) (which_bp); + +		printf ("Breakpoint %d removed\n", which_bp); +		return; +	} + +	/* Show a list of breakpoints */ + +	if (strcmp (argv[1], "show") == 0) { +		for (which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp) { + +			switch (which_bp) { +			case 1: +				addr = GET_IAC1 (); +				break; +			case 2: +				addr = GET_IAC2 (); +				break; +			case 3: +				addr = GET_IAC3 (); +				break; +			case 4: +				addr = GET_IAC4 (); +				break; +			} + +			printf ("Breakpoint [%d]: ", which_bp); +			if (addr == 0) +				printf ("NOT SET\n"); +			else +				disppc ((unsigned char *) addr, 0, 1, bedbug_puts, +						F_RADHEX); +		} +		return; +	} + +	/* Set a breakpoint at the address */ + +	if (!isdigit (argv[1][0])) { +		cmd_usage(cmdtp); +		return; +	} + +	addr = simple_strtoul (argv[1], NULL, 16) & 0xfffffffc; + +	if ((bug_ctx.set) && (which_bp = (*bug_ctx.set) (0, addr)) > 0) { +		printf ("Breakpoint [%d]: ", which_bp); +		disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); +	} + +	return; +}	/* bedbug405_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint.	 First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop.	Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug405_break_isr (struct pt_regs *regs) +{ +	unsigned long dbsr_val;		/* Value of the DBSR    */ +	unsigned long addr = 0;		/* Address stopped at   */ + +	/* -------------------------------------------------- */ + +	dbsr_val = GET_DBSR (); + +	if (dbsr_val & DBSR_IA1) { +		bug_ctx.current_bp = 1; +		addr = GET_IAC1 (); +		SET_DBSR (DBSR_IA1);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA2) { +		bug_ctx.current_bp = 2; +		addr = GET_IAC2 (); +		SET_DBSR (DBSR_IA2);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA3) { +		bug_ctx.current_bp = 3; +		addr = GET_IAC3 (); +		SET_DBSR (DBSR_IA3);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA4) { +		bug_ctx.current_bp = 4; +		addr = GET_IAC4 (); +		SET_DBSR (DBSR_IA4);	/* Write a 1 to clear */ +	} + +	bedbug_main_loop (addr, regs); +	return; +}	/* bedbug405_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug405_find_empty (void) +{ +	/* -------------------------------------------------- */ + +	if (GET_IAC1 () == 0) +		return 1; + +	if (GET_IAC2 () == 0) +		return 2; + +	if (GET_IAC3 () == 0) +		return 3; + +	if (GET_IAC4 () == 0) +		return 4; + +	return 0; +}	/* bedbug405_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint.  If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint.  If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0.  Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_set (int which_bp, unsigned long addr) +{ +	/* -------------------------------------------------- */ + +	/* Only look if which_bp == 0, else use which_bp */ +	if ((bug_ctx.find_empty) && (!which_bp) && +		(which_bp = (*bug_ctx.find_empty) ()) == 0) { +		printf ("All breakpoints in use\n"); +		return 0; +	} + +	if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { +		printf ("Invalid break point # %d\n", which_bp); +		return 0; +	} + +	if (!bug_ctx.hw_debug_enabled) { +		SET_MSR (GET_MSR () | 0x200);	/* set MSR[ DE ] */ +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IDM); +		bug_ctx.hw_debug_enabled = 1; +	} + +	switch (which_bp) { +	case 1: +		SET_IAC1 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA1); +		break; + +	case 2: +		SET_IAC2 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA2); +		break; + +	case 3: +		SET_IAC3 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA3); +		break; + +	case 4: +		SET_IAC4 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA4); +		break; +	} + +	return which_bp; +}	/* bedbug405_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_clear (int which_bp) +{ +	/* -------------------------------------------------- */ + +	if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { +		printf ("Invalid break point # (%d)\n", which_bp); +		return -1; +	} + +	switch (which_bp) { +	case 1: +		SET_IAC1 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA1); +		break; + +	case 2: +		SET_IAC2 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA2); +		break; + +	case 3: +		SET_IAC3 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA3); +		break; + +	case 4: +		SET_IAC4 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA4); +		break; +	} + +	return 0; +}	/* bedbug405_clear */ + + +/* ====================================================================== */ +#endif |