PPC: booke timers
While working on the emulation of the freescale p2010 (e500v2) I realized that there's no implementation of booke's timers features. Currently mpc8544 uses ppc_emb (ppc_emb_timers_init) which is close but not exactly like booke (for example booke uses different SPR). Signed-off-by: Fabien Chouteau <chouteau@adacore.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
		
							parent
							
								
									94135e813c
								
							
						
					
					
						commit
						ddd1055b07
					
				| @ -229,7 +229,7 @@ obj-i386-$(CONFIG_KVM) += kvmclock.o | ||||
| obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o | ||||
| 
 | ||||
| # shared objects
 | ||||
| obj-ppc-y = ppc.o | ||||
| obj-ppc-y = ppc.o ppc_booke.o | ||||
| obj-ppc-y += vga.o | ||||
| # PREP target
 | ||||
| obj-ppc-y += i8259.o mc146818rtc.o | ||||
|  | ||||
							
								
								
									
										138
									
								
								hw/ppc.c
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								hw/ppc.c
									
									
									
									
									
								
							| @ -50,7 +50,7 @@ | ||||
| static void cpu_ppc_tb_stop (CPUState *env); | ||||
| static void cpu_ppc_tb_start (CPUState *env); | ||||
| 
 | ||||
| static void ppc_set_irq (CPUState *env, int n_IRQ, int level) | ||||
| void ppc_set_irq(CPUState *env, int n_IRQ, int level) | ||||
| { | ||||
|     unsigned int old_pending = env->pending_interrupts; | ||||
| 
 | ||||
| @ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env) | ||||
| } | ||||
| /*****************************************************************************/ | ||||
| /* PowerPC time base and decrementer emulation */ | ||||
| struct ppc_tb_t { | ||||
|     /* Time base management */ | ||||
|     int64_t  tb_offset;    /* Compensation                    */ | ||||
|     int64_t  atb_offset;   /* Compensation                    */ | ||||
|     uint32_t tb_freq;      /* TB frequency                    */ | ||||
|     /* Decrementer management */ | ||||
|     uint64_t decr_next;    /* Tick for next decr interrupt    */ | ||||
|     uint32_t decr_freq;    /* decrementer frequency           */ | ||||
|     struct QEMUTimer *decr_timer; | ||||
|     /* Hypervisor decrementer management */ | ||||
|     uint64_t hdecr_next;    /* Tick for next hdecr interrupt  */ | ||||
|     struct QEMUTimer *hdecr_timer; | ||||
|     uint64_t purr_load; | ||||
|     uint64_t purr_start; | ||||
|     void *opaque; | ||||
| }; | ||||
| 
 | ||||
| static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, | ||||
|                                       int64_t tb_offset) | ||||
| uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) | ||||
| { | ||||
|     /* TB time in tb periods */ | ||||
|     return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; | ||||
| @ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next) | ||||
|     int64_t diff; | ||||
| 
 | ||||
|     diff = next - qemu_get_clock_ns(vm_clock); | ||||
|     if (diff >= 0) | ||||
|     if (diff >= 0) { | ||||
|         decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); | ||||
|     else | ||||
|     } else if (tb_env->flags & PPC_TIMER_BOOKE) { | ||||
|         decr = 0; | ||||
|     }  else { | ||||
|         decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec()); | ||||
|     } | ||||
|     LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); | ||||
| 
 | ||||
|     return decr; | ||||
| @ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, | ||||
|                 decr, value); | ||||
|     now = qemu_get_clock_ns(vm_clock); | ||||
|     next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); | ||||
|     if (is_excp) | ||||
|     if (is_excp) { | ||||
|         next += *nextp - now; | ||||
|     if (next == now) | ||||
|     } | ||||
|     if (next == now) { | ||||
|         next++; | ||||
|     } | ||||
|     *nextp = next; | ||||
|     /* Adjust timer */ | ||||
|     qemu_mod_timer(timer, next); | ||||
|     /* If we set a negative value and the decrementer was positive,
 | ||||
|      * raise an exception. | ||||
| 
 | ||||
|     /* If we set a negative value and the decrementer was positive, raise an
 | ||||
|      * exception. | ||||
|      */ | ||||
|     if ((value & 0x80000000) && !(decr & 0x80000000)) | ||||
|     if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) | ||||
|         && (value & 0x80000000) | ||||
|         && !(decr & 0x80000000)) { | ||||
|         (*raise_excp)(env); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr, | ||||
| @ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) | ||||
| 
 | ||||
|     tb_env = g_malloc0(sizeof(ppc_tb_t)); | ||||
|     env->tb_env = tb_env; | ||||
|     tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; | ||||
|     /* Create new timer */ | ||||
|     tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env); | ||||
|     if (0) { | ||||
| @ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env) | ||||
| } | ||||
| 
 | ||||
| /*****************************************************************************/ | ||||
| /* Embedded PowerPC timers */ | ||||
| /* PowerPC 40x timers */ | ||||
| 
 | ||||
| /* PIT, FIT & WDT */ | ||||
| typedef struct ppcemb_timer_t ppcemb_timer_t; | ||||
| struct ppcemb_timer_t { | ||||
| typedef struct ppc40x_timer_t ppc40x_timer_t; | ||||
| struct ppc40x_timer_t { | ||||
|     uint64_t pit_reload;  /* PIT auto-reload value        */ | ||||
|     uint64_t fit_next;    /* Tick for next FIT interrupt  */ | ||||
|     struct QEMUTimer *fit_timer; | ||||
| @ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
|     uint64_t now, next; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
|     ppc40x_timer = tb_env->opaque; | ||||
|     now = qemu_get_clock_ns(vm_clock); | ||||
|     switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { | ||||
|     case 0: | ||||
| @ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque) | ||||
|     next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); | ||||
|     if (next == now) | ||||
|         next++; | ||||
|     qemu_mod_timer(ppcemb_timer->fit_timer, next); | ||||
|     qemu_mod_timer(ppc40x_timer->fit_timer, next); | ||||
|     env->spr[SPR_40x_TSR] |= 1 << 26; | ||||
|     if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) | ||||
|         ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); | ||||
| @ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque) | ||||
| /* Programmable interval timer */ | ||||
| static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) | ||||
| { | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
|     uint64_t now, next; | ||||
| 
 | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
|     if (ppcemb_timer->pit_reload <= 1 || | ||||
|     ppc40x_timer = tb_env->opaque; | ||||
|     if (ppc40x_timer->pit_reload <= 1 || | ||||
|         !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || | ||||
|         (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { | ||||
|         /* Stop PIT */ | ||||
| @ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) | ||||
|         qemu_del_timer(tb_env->decr_timer); | ||||
|     } else { | ||||
|         LOG_TB("%s: start PIT %016" PRIx64 "\n", | ||||
|                     __func__, ppcemb_timer->pit_reload); | ||||
|                     __func__, ppc40x_timer->pit_reload); | ||||
|         now = qemu_get_clock_ns(vm_clock); | ||||
|         next = now + muldiv64(ppcemb_timer->pit_reload, | ||||
|         next = now + muldiv64(ppc40x_timer->pit_reload, | ||||
|                               get_ticks_per_sec(), tb_env->decr_freq); | ||||
|         if (is_excp) | ||||
|             next += tb_env->decr_next - now; | ||||
| @ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
|     ppc40x_timer = tb_env->opaque; | ||||
|     env->spr[SPR_40x_TSR] |= 1 << 27; | ||||
|     if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) | ||||
|         ppc_set_irq(env, ppcemb_timer->decr_excp, 1); | ||||
|         ppc_set_irq(env, ppc40x_timer->decr_excp, 1); | ||||
|     start_stop_pit(env, tb_env, 1); | ||||
|     LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " | ||||
|            "%016" PRIx64 "\n", __func__, | ||||
|            (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), | ||||
|            (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), | ||||
|            env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], | ||||
|            ppcemb_timer->pit_reload); | ||||
|            ppc40x_timer->pit_reload); | ||||
| } | ||||
| 
 | ||||
| /* Watchdog timer */ | ||||
| @ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
|     uint64_t now, next; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
|     ppc40x_timer = tb_env->opaque; | ||||
|     now = qemu_get_clock_ns(vm_clock); | ||||
|     switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { | ||||
|     case 0: | ||||
| @ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque) | ||||
|     switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { | ||||
|     case 0x0: | ||||
|     case 0x1: | ||||
|         qemu_mod_timer(ppcemb_timer->wdt_timer, next); | ||||
|         ppcemb_timer->wdt_next = next; | ||||
|         qemu_mod_timer(ppc40x_timer->wdt_timer, next); | ||||
|         ppc40x_timer->wdt_next = next; | ||||
|         env->spr[SPR_40x_TSR] |= 1 << 31; | ||||
|         break; | ||||
|     case 0x2: | ||||
|         qemu_mod_timer(ppcemb_timer->wdt_timer, next); | ||||
|         ppcemb_timer->wdt_next = next; | ||||
|         qemu_mod_timer(ppc40x_timer->wdt_timer, next); | ||||
|         ppc40x_timer->wdt_next = next; | ||||
|         env->spr[SPR_40x_TSR] |= 1 << 30; | ||||
|         if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) | ||||
|             ppc_set_irq(env, PPC_INTERRUPT_WDT, 1); | ||||
| @ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque) | ||||
| void store_40x_pit (CPUState *env, target_ulong val) | ||||
| { | ||||
|     ppc_tb_t *tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
| 
 | ||||
|     tb_env = env->tb_env; | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
|     ppc40x_timer = tb_env->opaque; | ||||
|     LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); | ||||
|     ppcemb_timer->pit_reload = val; | ||||
|     ppc40x_timer->pit_reload = val; | ||||
|     start_stop_pit(env, tb_env, 0); | ||||
| } | ||||
| 
 | ||||
| @ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env) | ||||
|     return cpu_ppc_load_decr(env); | ||||
| } | ||||
| 
 | ||||
| void store_booke_tsr (CPUState *env, target_ulong val) | ||||
| { | ||||
|     ppc_tb_t *tb_env = env->tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
| 
 | ||||
|     ppcemb_timer = tb_env->opaque; | ||||
| 
 | ||||
|     LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); | ||||
|     env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000); | ||||
|     if (val & 0x80000000) | ||||
|         ppc_set_irq(env, ppcemb_timer->decr_excp, 0); | ||||
| } | ||||
| 
 | ||||
| void store_booke_tcr (CPUState *env, target_ulong val) | ||||
| { | ||||
|     ppc_tb_t *tb_env; | ||||
| 
 | ||||
|     tb_env = env->tb_env; | ||||
|     LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); | ||||
|     env->spr[SPR_40x_TCR] = val & 0xFFC00000; | ||||
|     start_stop_pit(env, tb_env, 1); | ||||
|     cpu_4xx_wdt_cb(env); | ||||
| } | ||||
| 
 | ||||
| static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) | ||||
| static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) | ||||
| { | ||||
|     CPUState *env = opaque; | ||||
|     ppc_tb_t *tb_env = env->tb_env; | ||||
| @ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) | ||||
|     /* XXX: we should also update all timers */ | ||||
| } | ||||
| 
 | ||||
| clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, | ||||
| clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, | ||||
|                                   unsigned int decr_excp) | ||||
| { | ||||
|     ppc_tb_t *tb_env; | ||||
|     ppcemb_timer_t *ppcemb_timer; | ||||
|     ppc40x_timer_t *ppc40x_timer; | ||||
| 
 | ||||
|     tb_env = g_malloc0(sizeof(ppc_tb_t)); | ||||
|     env->tb_env = tb_env; | ||||
|     ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t)); | ||||
|     tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; | ||||
|     ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); | ||||
|     tb_env->tb_freq = freq; | ||||
|     tb_env->decr_freq = freq; | ||||
|     tb_env->opaque = ppcemb_timer; | ||||
|     tb_env->opaque = ppc40x_timer; | ||||
|     LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); | ||||
|     if (ppcemb_timer != NULL) { | ||||
|     if (ppc40x_timer != NULL) { | ||||
|         /* We use decr timer for PIT */ | ||||
|         tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env); | ||||
|         ppcemb_timer->fit_timer = | ||||
|         ppc40x_timer->fit_timer = | ||||
|             qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env); | ||||
|         ppcemb_timer->wdt_timer = | ||||
|         ppc40x_timer->wdt_timer = | ||||
|             qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env); | ||||
|         ppcemb_timer->decr_excp = decr_excp; | ||||
|         ppc40x_timer->decr_excp = decr_excp; | ||||
|     } | ||||
| 
 | ||||
|     return &ppc_emb_set_tb_clk; | ||||
|     return &ppc_40x_set_tb_clk; | ||||
| } | ||||
| 
 | ||||
| /*****************************************************************************/ | ||||
|  | ||||
							
								
								
									
										37
									
								
								hw/ppc.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								hw/ppc.h
									
									
									
									
									
								
							| @ -1,3 +1,5 @@ | ||||
| void ppc_set_irq (CPUState *env, int n_IRQ, int level); | ||||
| 
 | ||||
| /* PowerPC hardware exceptions management helpers */ | ||||
| typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); | ||||
| typedef struct clk_setup_t clk_setup_t; | ||||
| @ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq) | ||||
|         (*clk->cb)(clk->opaque, freq); | ||||
| } | ||||
| 
 | ||||
| struct ppc_tb_t { | ||||
|     /* Time base management */ | ||||
|     int64_t  tb_offset;    /* Compensation                    */ | ||||
|     int64_t  atb_offset;   /* Compensation                    */ | ||||
|     uint32_t tb_freq;      /* TB frequency                    */ | ||||
|     /* Decrementer management */ | ||||
|     uint64_t decr_next;    /* Tick for next decr interrupt    */ | ||||
|     uint32_t decr_freq;    /* decrementer frequency           */ | ||||
|     struct QEMUTimer *decr_timer; | ||||
|     /* Hypervisor decrementer management */ | ||||
|     uint64_t hdecr_next;    /* Tick for next hdecr interrupt  */ | ||||
|     struct QEMUTimer *hdecr_timer; | ||||
|     uint64_t purr_load; | ||||
|     uint64_t purr_start; | ||||
|     void *opaque; | ||||
|     uint32_t flags; | ||||
| }; | ||||
| 
 | ||||
| /* PPC Timers flags */ | ||||
| #define PPC_TIMER_BOOKE              (1 << 0) /* Enable Booke support */ | ||||
| #define PPC_TIMER_E500               (1 << 1) /* Enable e500 support */ | ||||
| #define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when | ||||
|                                                * the most significant bit | ||||
|                                                * changes from 0 to 1. | ||||
|                                                */ | ||||
| #define PPC_DECR_ZERO_TRIGGERED      (1 << 3) /* Decr interrupt triggered when | ||||
|                                                * the decrementer reaches zero. | ||||
|                                                */ | ||||
| 
 | ||||
| uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); | ||||
| clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq); | ||||
| /* Embedded PowerPC DCR management */ | ||||
| typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); | ||||
| @ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn), | ||||
|                   int (*dcr_write_error)(int dcrn)); | ||||
| int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, | ||||
|                       dcr_read_cb drc_read, dcr_write_cb dcr_write); | ||||
| clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, | ||||
| clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, | ||||
|                                   unsigned int decr_excp); | ||||
| 
 | ||||
| /* Embedded PowerPC reset */ | ||||
| @ -55,3 +87,6 @@ enum { | ||||
| #define FW_CFG_PPC_KVM_PID      (FW_CFG_ARCH_LOCAL + 0x07) | ||||
| 
 | ||||
| #define PPC_SERIAL_MM_BAUDBASE 399193 | ||||
| 
 | ||||
| /* ppc_booke.c */ | ||||
| void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags); | ||||
|  | ||||
| @ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model, | ||||
|     cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ | ||||
|     cpu_clk->opaque = env; | ||||
|     /* Set time-base frequency to sysclk */ | ||||
|     tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT); | ||||
|     tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); | ||||
|     tb_clk->opaque = env; | ||||
|     ppc_dcr_init(env, NULL, NULL); | ||||
|     /* Register qemu callbacks */ | ||||
|  | ||||
							
								
								
									
										266
									
								
								hw/ppc_booke.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								hw/ppc_booke.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,266 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC Booke hardware System Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011 AdaCore | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "hw.h" | ||||
| #include "ppc.h" | ||||
| #include "qemu-timer.h" | ||||
| #include "sysemu.h" | ||||
| #include "nvram.h" | ||||
| #include "qemu-log.h" | ||||
| #include "loader.h" | ||||
| 
 | ||||
| 
 | ||||
| /* Timer Control Register */ | ||||
| 
 | ||||
| #define TCR_WP_SHIFT  30        /* Watchdog Timer Period */ | ||||
| #define TCR_WP_MASK   (0x3 << TCR_WP_SHIFT) | ||||
| #define TCR_WRC_SHIFT 28        /* Watchdog Timer Reset Control */ | ||||
| #define TCR_WRC_MASK  (0x3 << TCR_WRC_SHIFT) | ||||
| #define TCR_WIE       (1 << 27) /* Watchdog Timer Interrupt Enable */ | ||||
| #define TCR_DIE       (1 << 26) /* Decrementer Interrupt Enable */ | ||||
| #define TCR_FP_SHIFT  24        /* Fixed-Interval Timer Period */ | ||||
| #define TCR_FP_MASK   (0x3 << TCR_FP_SHIFT) | ||||
| #define TCR_FIE       (1 << 23) /* Fixed-Interval Timer Interrupt Enable */ | ||||
| #define TCR_ARE       (1 << 22) /* Auto-Reload Enable */ | ||||
| 
 | ||||
| /* Timer Control Register (e500 specific fields) */ | ||||
| 
 | ||||
| #define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */ | ||||
| #define TCR_E500_FPEXT_MASK  (0xf << TCR_E500_FPEXT_SHIFT) | ||||
| #define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */ | ||||
| #define TCR_E500_WPEXT_MASK  (0xf << TCR_E500_WPEXT_SHIFT) | ||||
| 
 | ||||
| /* Timer Status Register  */ | ||||
| 
 | ||||
| #define TSR_FIS       (1 << 26) /* Fixed-Interval Timer Interrupt Status */ | ||||
| #define TSR_DIS       (1 << 27) /* Decrementer Interrupt Status */ | ||||
| #define TSR_WRS_SHIFT 28        /* Watchdog Timer Reset Status */ | ||||
| #define TSR_WRS_MASK  (0x3 << TSR_WRS_SHIFT) | ||||
| #define TSR_WIS       (1 << 30) /* Watchdog Timer Interrupt Status */ | ||||
| #define TSR_ENW       (1 << 31) /* Enable Next Watchdog Timer */ | ||||
| 
 | ||||
| typedef struct booke_timer_t booke_timer_t; | ||||
| struct booke_timer_t { | ||||
| 
 | ||||
|     uint64_t fit_next; | ||||
|     struct QEMUTimer *fit_timer; | ||||
| 
 | ||||
|     uint64_t wdt_next; | ||||
|     struct QEMUTimer *wdt_timer; | ||||
| 
 | ||||
|     uint32_t flags; | ||||
| }; | ||||
| 
 | ||||
| static void booke_update_irq(CPUState *env) | ||||
| { | ||||
|     ppc_set_irq(env, PPC_INTERRUPT_DECR, | ||||
|                 (env->spr[SPR_BOOKE_TSR] & TSR_DIS | ||||
|                  && env->spr[SPR_BOOKE_TCR] & TCR_DIE)); | ||||
| 
 | ||||
|     ppc_set_irq(env, PPC_INTERRUPT_WDT, | ||||
|                 (env->spr[SPR_BOOKE_TSR] & TSR_WIS | ||||
|                  && env->spr[SPR_BOOKE_TCR] & TCR_WIE)); | ||||
| 
 | ||||
|     ppc_set_irq(env, PPC_INTERRUPT_FIT, | ||||
|                 (env->spr[SPR_BOOKE_TSR] & TSR_FIS | ||||
|                  && env->spr[SPR_BOOKE_TCR] & TCR_FIE)); | ||||
| } | ||||
| 
 | ||||
| /* Return the location of the bit of time base at which the FIT will raise an
 | ||||
|    interrupt */ | ||||
| static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env) | ||||
| { | ||||
|     uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT; | ||||
| 
 | ||||
|     if (tb_env->flags & PPC_TIMER_E500) { | ||||
|         /* e500 Fixed-interval timer period extension */ | ||||
|         uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK) | ||||
|             >> TCR_E500_FPEXT_SHIFT; | ||||
|         fp = 63 - (fp | fpext << 2); | ||||
|     } else { | ||||
|         fp = env->fit_period[fp]; | ||||
|     } | ||||
| 
 | ||||
|     return fp; | ||||
| } | ||||
| 
 | ||||
| /* Return the location of the bit of time base at which the WDT will raise an
 | ||||
|    interrupt */ | ||||
| static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env) | ||||
| { | ||||
|     uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT; | ||||
| 
 | ||||
|     if (tb_env->flags & PPC_TIMER_E500) { | ||||
|         /* e500 Watchdog timer period extension */ | ||||
|         uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK) | ||||
|             >> TCR_E500_WPEXT_SHIFT; | ||||
|         wp = 63 - (wp | wpext << 2); | ||||
|     } else { | ||||
|         wp = env->wdt_period[wp]; | ||||
|     } | ||||
| 
 | ||||
|     return wp; | ||||
| } | ||||
| 
 | ||||
| static void booke_update_fixed_timer(CPUState         *env, | ||||
|                                      uint8_t           target_bit, | ||||
|                                      uint64_t          *next, | ||||
|                                      struct QEMUTimer *timer) | ||||
| { | ||||
|     ppc_tb_t *tb_env = env->tb_env; | ||||
|     uint64_t lapse; | ||||
|     uint64_t tb; | ||||
|     uint64_t period = 1 << (target_bit + 1); | ||||
|     uint64_t now; | ||||
| 
 | ||||
|     now = qemu_get_clock_ns(vm_clock); | ||||
|     tb  = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset); | ||||
| 
 | ||||
|     lapse = period - ((tb - (1 << target_bit)) & (period - 1)); | ||||
| 
 | ||||
|     *next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq); | ||||
| 
 | ||||
|     /* XXX: If expire time is now. We can't run the callback because we don't
 | ||||
|      * have access to it. So we just set the timer one nanosecond later. | ||||
|      */ | ||||
| 
 | ||||
|     if (*next == now) { | ||||
|         (*next)++; | ||||
|     } | ||||
| 
 | ||||
|     qemu_mod_timer(timer, *next); | ||||
| } | ||||
| 
 | ||||
| static void booke_decr_cb(void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     booke_timer_t *booke_timer; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     booke_timer = tb_env->opaque; | ||||
|     env->spr[SPR_BOOKE_TSR] |= TSR_DIS; | ||||
| 
 | ||||
|     booke_update_irq(env); | ||||
| 
 | ||||
|     if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { | ||||
|         /* Auto Reload */ | ||||
|         cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void booke_fit_cb(void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     booke_timer_t *booke_timer; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     booke_timer = tb_env->opaque; | ||||
|     env->spr[SPR_BOOKE_TSR] |= TSR_FIS; | ||||
| 
 | ||||
|     booke_update_irq(env); | ||||
| 
 | ||||
|     booke_update_fixed_timer(env, | ||||
|                              booke_get_fit_target(env, tb_env), | ||||
|                              &booke_timer->fit_next, | ||||
|                              booke_timer->fit_timer); | ||||
| } | ||||
| 
 | ||||
| static void booke_wdt_cb(void *opaque) | ||||
| { | ||||
|     CPUState *env; | ||||
|     ppc_tb_t *tb_env; | ||||
|     booke_timer_t *booke_timer; | ||||
| 
 | ||||
|     env = opaque; | ||||
|     tb_env = env->tb_env; | ||||
|     booke_timer = tb_env->opaque; | ||||
| 
 | ||||
|     /* TODO: There's lots of complicated stuff to do here */ | ||||
| 
 | ||||
|     booke_update_irq(env); | ||||
| 
 | ||||
|     booke_update_fixed_timer(env, | ||||
|                              booke_get_wdt_target(env, tb_env), | ||||
|                              &booke_timer->wdt_next, | ||||
|                              booke_timer->wdt_timer); | ||||
| } | ||||
| 
 | ||||
| void store_booke_tsr(CPUState *env, target_ulong val) | ||||
| { | ||||
|     ppc_tb_t *tb_env = env->tb_env; | ||||
|     booke_timer_t *booke_timer; | ||||
| 
 | ||||
|     booke_timer = tb_env->opaque; | ||||
| 
 | ||||
|     env->spr[SPR_BOOKE_TSR] &= ~val; | ||||
| 
 | ||||
|     booke_update_irq(env); | ||||
| } | ||||
| 
 | ||||
| void store_booke_tcr(CPUState *env, target_ulong val) | ||||
| { | ||||
|     ppc_tb_t *tb_env = env->tb_env; | ||||
|     booke_timer_t *booke_timer = tb_env->opaque; | ||||
| 
 | ||||
|     tb_env = env->tb_env; | ||||
|     env->spr[SPR_BOOKE_TCR] = val; | ||||
| 
 | ||||
|     booke_update_irq(env); | ||||
| 
 | ||||
|     booke_update_fixed_timer(env, | ||||
|                              booke_get_fit_target(env, tb_env), | ||||
|                              &booke_timer->fit_next, | ||||
|                              booke_timer->fit_timer); | ||||
| 
 | ||||
|     booke_update_fixed_timer(env, | ||||
|                              booke_get_wdt_target(env, tb_env), | ||||
|                              &booke_timer->wdt_next, | ||||
|                              booke_timer->wdt_timer); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags) | ||||
| { | ||||
|     ppc_tb_t *tb_env; | ||||
|     booke_timer_t *booke_timer; | ||||
| 
 | ||||
|     tb_env      = g_malloc0(sizeof(ppc_tb_t)); | ||||
|     booke_timer = g_malloc0(sizeof(booke_timer_t)); | ||||
| 
 | ||||
|     env->tb_env = tb_env; | ||||
|     tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; | ||||
| 
 | ||||
|     tb_env->tb_freq    = freq; | ||||
|     tb_env->decr_freq  = freq; | ||||
|     tb_env->opaque     = booke_timer; | ||||
|     tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env); | ||||
| 
 | ||||
|     booke_timer->fit_timer = | ||||
|         qemu_new_timer_ns(vm_clock, &booke_fit_cb, env); | ||||
|     booke_timer->wdt_timer = | ||||
|         qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env); | ||||
| } | ||||
| @ -268,11 +268,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, | ||||
|         irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; | ||||
|         env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; | ||||
| 
 | ||||
|         /* XXX register timer? */ | ||||
|         ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); | ||||
|         ppc_dcr_init(env, NULL, NULL); | ||||
|         /* XXX Enable DEC interrupts - probably wrong in the backend */ | ||||
|         env->spr[SPR_40x_TCR] = 1 << 26; | ||||
|         ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); | ||||
| 
 | ||||
|         /* Register reset handler */ | ||||
|         if (!i) { | ||||
|  | ||||
| @ -81,7 +81,6 @@ static void mmubooke_create_initial_mapping(CPUState *env, | ||||
| static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, | ||||
|                                     int do_init, | ||||
|                                     const char *cpu_model, | ||||
|                                     clk_setup_t *cpu_clk, clk_setup_t *tb_clk, | ||||
|                                     uint32_t sysclk) | ||||
| { | ||||
|     CPUState *env; | ||||
| @ -93,11 +92,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ | ||||
|     cpu_clk->opaque = env; | ||||
|     /* Set time-base frequency to sysclk */ | ||||
|     tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR); | ||||
|     tb_clk->opaque = env; | ||||
|     ppc_booke_timers_init(env, sysclk, 0/* no flags */); | ||||
| 
 | ||||
|     ppc_dcr_init(env, NULL, NULL); | ||||
| 
 | ||||
| @ -197,7 +192,6 @@ static void virtex_init(ram_addr_t ram_size, | ||||
|     DriveInfo *dinfo; | ||||
|     ram_addr_t phys_ram; | ||||
|     qemu_irq irq[32], *cpu_irq; | ||||
|     clk_setup_t clk_setup[7]; | ||||
|     int kernel_size; | ||||
|     int i; | ||||
| 
 | ||||
| @ -207,8 +201,7 @@ static void virtex_init(ram_addr_t ram_size, | ||||
|     } | ||||
| 
 | ||||
|     memset(clk_setup, 0, sizeof(clk_setup)); | ||||
|     env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0], | ||||
|                              &clk_setup[1], 400000000); | ||||
|     env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000); | ||||
|     qemu_register_reset(main_cpu_reset, env); | ||||
| 
 | ||||
|     phys_ram = qemu_ram_alloc(NULL, "ram", ram_size); | ||||
|  | ||||
| @ -1018,8 +1018,35 @@ struct CPUPPCState { | ||||
| #if !defined(CONFIG_USER_ONLY) | ||||
|     void *load_info;    /* Holds boot loading state.  */ | ||||
| #endif | ||||
| 
 | ||||
|     /* booke timers */ | ||||
| 
 | ||||
|     /* Specifies bit locations of the Time Base used to signal a fixed timer
 | ||||
|      * exception on a transition from 0 to 1. (watchdog or fixed-interval timer) | ||||
|      * | ||||
|      * 0 selects the least significant bit. | ||||
|      * 63 selects the most significant bit. | ||||
|      */ | ||||
|     uint8_t fit_period[4]; | ||||
|     uint8_t wdt_period[4]; | ||||
| }; | ||||
| 
 | ||||
| #define SET_FIT_PERIOD(a_, b_, c_, d_)          \ | ||||
| do {                                            \ | ||||
|     env->fit_period[0] = (a_);                  \ | ||||
|     env->fit_period[1] = (b_);                  \ | ||||
|     env->fit_period[2] = (c_);                  \ | ||||
|     env->fit_period[3] = (d_);                  \ | ||||
|  } while (0) | ||||
| 
 | ||||
| #define SET_WDT_PERIOD(a_, b_, c_, d_)          \ | ||||
| do {                                            \ | ||||
|     env->wdt_period[0] = (a_);                  \ | ||||
|     env->wdt_period[1] = (b_);                  \ | ||||
|     env->wdt_period[2] = (c_);                  \ | ||||
|     env->wdt_period[3] = (d_);                  \ | ||||
|  } while (0) | ||||
| 
 | ||||
| #if !defined(CONFIG_USER_ONLY) | ||||
| /* Context used internally during MMU translations */ | ||||
| typedef struct mmu_ctx_t mmu_ctx_t; | ||||
|  | ||||
| @ -3266,6 +3266,9 @@ static void init_proc_401 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 401x2                                                             */ | ||||
| @ -3304,6 +3307,9 @@ static void init_proc_401x2 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 401x3                                                             */ | ||||
| @ -3337,6 +3343,9 @@ static void init_proc_401x3 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* IOP480                                                                    */ | ||||
| @ -3375,6 +3384,9 @@ static void init_proc_IOP480 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(8, 12, 16, 20); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 403                                                               */ | ||||
| @ -3405,6 +3417,9 @@ static void init_proc_403 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(8, 12, 16, 20); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 403 GCX                                                           */ | ||||
| @ -3455,6 +3470,9 @@ static void init_proc_403GCX (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(8, 12, 16, 20); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 405                                                               */ | ||||
| @ -3504,6 +3522,9 @@ static void init_proc_405 (CPUPPCState *env) | ||||
|     env->icache_line_size = 32; | ||||
|     /* Allocate hardware IRQ controller */ | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(8, 12, 16, 20); | ||||
|     SET_WDT_PERIOD(16, 20, 24, 28); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 440 EP                                                            */ | ||||
| @ -3586,6 +3607,9 @@ static void init_proc_440EP (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     /* XXX: TODO: allocate internal IRQ controller */ | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 440 GP                                                            */ | ||||
| @ -3650,6 +3674,9 @@ static void init_proc_440GP (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     /* XXX: TODO: allocate internal IRQ controller */ | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 440x4                                                             */ | ||||
| @ -3714,6 +3741,9 @@ static void init_proc_440x4 (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     /* XXX: TODO: allocate internal IRQ controller */ | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 440x5                                                             */ | ||||
| @ -3795,6 +3825,9 @@ static void init_proc_440x5 (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     ppc40x_irq_init(env); | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 460 (guessed)                                                     */ | ||||
| @ -3883,6 +3916,9 @@ static void init_proc_460 (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     /* XXX: TODO: allocate internal IRQ controller */ | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* PowerPC 460F (guessed)                                                    */ | ||||
| @ -3974,6 +4010,9 @@ static void init_proc_460F (CPUPPCState *env) | ||||
|     env->dcache_line_size = 32; | ||||
|     env->icache_line_size = 32; | ||||
|     /* XXX: TODO: allocate internal IRQ controller */ | ||||
| 
 | ||||
|     SET_FIT_PERIOD(12, 16, 20, 24); | ||||
|     SET_WDT_PERIOD(20, 24, 28, 32); | ||||
| } | ||||
| 
 | ||||
| /* Freescale 5xx cores (aka RCPU) */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Fabien Chouteau
						Fabien Chouteau