+ return &cpu_ppc_set_tb_clk;
+}
+
+/* Specific helpers for POWER & PowerPC 601 RTC */
+clk_setup_cb cpu_ppc601_rtc_init (CPUState *env)
+{
+ return cpu_ppc_tb_init(env, 7812500);
+}
+
+void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_tbu(env, value);
+}
+
+uint32_t cpu_ppc601_load_rtcu (CPUState *env)
+{
+ return _cpu_ppc_load_tbu(env);
+}
+
+void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value)
+{
+ cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
+}
+
+uint32_t cpu_ppc601_load_rtcl (CPUState *env)
+{
+ return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
+}
+
+/*****************************************************************************/
+/* Embedded PowerPC timers */
+
+/* PIT, FIT & WDT */
+typedef struct ppcemb_timer_t ppcemb_timer_t;
+struct ppcemb_timer_t {
+ uint64_t pit_reload; /* PIT auto-reload value */
+ uint64_t fit_next; /* Tick for next FIT interrupt */
+ struct QEMUTimer *fit_timer;
+ uint64_t wdt_next; /* Tick for next WDT interrupt */
+ struct QEMUTimer *wdt_timer;
+};
+
+/* Fixed interval timer */
+static void cpu_4xx_fit_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
+ case 0:
+ next = 1 << 9;
+ break;
+ case 1:
+ next = 1 << 13;
+ break;
+ case 2:
+ next = 1 << 17;
+ break;
+ case 3:
+ next = 1 << 21;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
+ if (next == now)
+ next++;
+ qemu_mod_timer(ppcemb_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);
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: ir %d TCR " ADDRX " TSR " ADDRX "\n", __func__,
+ (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1),
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+ }
+#endif
+}
+
+/* Programmable interval timer */
+static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
+{
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ ppcemb_timer = tb_env->opaque;
+ if (ppcemb_timer->pit_reload <= 1 ||
+ !((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
+ (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
+ /* Stop PIT */
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: stop PIT\n", __func__);
+ }
+#endif
+ qemu_del_timer(tb_env->decr_timer);
+ } else {
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: start PIT %016" PRIx64 "\n",
+ __func__, ppcemb_timer->pit_reload);
+ }
+#endif
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(ppcemb_timer->pit_reload,
+ ticks_per_sec, tb_env->decr_freq);
+ if (is_excp)
+ next += tb_env->decr_next - now;
+ if (next == now)
+ next++;
+ qemu_mod_timer(tb_env->decr_timer, next);
+ tb_env->decr_next = next;
+ }
+}
+
+static void cpu_4xx_pit_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ env->spr[SPR_40x_TSR] |= 1 << 27;
+ if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
+ start_stop_pit(env, tb_env, 1);
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: ar %d ir %d TCR " ADDRX " TSR " ADDRX " "
+ "%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);
+ }
+#endif
+}
+
+/* Watchdog timer */
+static void cpu_4xx_wdt_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
+ case 0:
+ next = 1 << 17;
+ break;
+ case 1:
+ next = 1 << 21;
+ break;
+ case 2:
+ next = 1 << 25;
+ break;
+ case 3:
+ next = 1 << 29;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, ticks_per_sec, tb_env->decr_freq);
+ if (next == now)
+ next++;
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: TCR " ADDRX " TSR " ADDRX "\n", __func__,
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+ }
+#endif
+ 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;
+ env->spr[SPR_40x_TSR] |= 1 << 31;
+ break;
+ case 0x2:
+ qemu_mod_timer(ppcemb_timer->wdt_timer, next);
+ ppcemb_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);
+ break;
+ case 0x3:
+ env->spr[SPR_40x_TSR] &= ~0x30000000;
+ env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
+ switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
+ case 0x0:
+ /* No reset */
+ break;
+ case 0x1: /* Core reset */
+ ppc40x_core_reset(env);
+ break;
+ case 0x2: /* Chip reset */
+ ppc40x_chip_reset(env);
+ break;
+ case 0x3: /* System reset */
+ ppc40x_system_reset(env);
+ break;
+ }
+ }
+}
+
+void store_40x_pit (CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s val" ADDRX "\n", __func__, val);
+ }
+#endif
+ ppcemb_timer->pit_reload = val;
+ start_stop_pit(env, tb_env, 0);
+}
+
+target_ulong load_40x_pit (CPUState *env)
+{
+ return cpu_ppc_load_decr(env);
+}
+
+void store_booke_tsr (CPUState *env, target_ulong val)
+{
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: val " ADDRX "\n", __func__, val);
+ }
+#endif
+ env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
+ if (val & 0x80000000)
+ ppc_set_irq(env, PPC_INTERRUPT_PIT, 0);
+}
+
+void store_booke_tcr (CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env;
+
+ tb_env = env->tb_env;
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: val " ADDRX "\n", __func__, val);
+ }
+#endif
+ 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)
+{
+ CPUState *env = opaque;
+ ppc_tb_t *tb_env = env->tb_env;
+
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s set new frequency to %" PRIu32 "\n", __func__,
+ freq);
+ }
+#endif
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ /* XXX: we should also update all timers */
+}
+
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq)
+{
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ tb_env = qemu_mallocz(sizeof(ppc_tb_t));
+ if (tb_env == NULL) {
+ return NULL;
+ }
+ env->tb_env = tb_env;
+ ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ tb_env->opaque = ppcemb_timer;
+#ifdef PPC_DEBUG_TB
+ if (loglevel != 0) {
+ fprintf(logfile, "%s freq %" PRIu32 "\n", __func__, freq);
+ }
+#endif
+ if (ppcemb_timer != NULL) {
+ /* We use decr timer for PIT */
+ tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
+ ppcemb_timer->fit_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
+ ppcemb_timer->wdt_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
+ }
+
+ return &ppc_emb_set_tb_clk;
+}
+
+/*****************************************************************************/
+/* Embedded PowerPC Device Control Registers */
+typedef struct ppc_dcrn_t ppc_dcrn_t;
+struct ppc_dcrn_t {
+ dcr_read_cb dcr_read;
+ dcr_write_cb dcr_write;
+ void *opaque;
+};
+
+/* XXX: on 460, DCR addresses are 32 bits wide,
+ * using DCRIPR to get the 22 upper bits of the DCR address
+ */
+#define DCRN_NB 1024
+struct ppc_dcr_t {
+ ppc_dcrn_t dcrn[DCRN_NB];
+ int (*read_error)(int dcrn);
+ int (*write_error)(int dcrn);
+};
+
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp)
+{
+ ppc_dcrn_t *dcr;
+
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ goto error;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->dcr_read == NULL)
+ goto error;
+ *valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
+
+ return 0;
+
+ error:
+ if (dcr_env->read_error != NULL)
+ return (*dcr_env->read_error)(dcrn);
+
+ return -1;
+}
+
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val)
+{
+ ppc_dcrn_t *dcr;
+
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ goto error;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->dcr_write == NULL)
+ goto error;
+ (*dcr->dcr_write)(dcr->opaque, dcrn, val);
+
+ return 0;
+
+ error:
+ if (dcr_env->write_error != NULL)
+ return (*dcr_env->write_error)(dcrn);
+
+ return -1;
+}
+
+int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
+ dcr_read_cb dcr_read, dcr_write_cb dcr_write)
+{
+ ppc_dcr_t *dcr_env;
+ ppc_dcrn_t *dcr;
+
+ dcr_env = env->dcr_env;
+ if (dcr_env == NULL)
+ return -1;
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ return -1;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->opaque != NULL ||
+ dcr->dcr_read != NULL ||
+ dcr->dcr_write != NULL)
+ return -1;
+ dcr->opaque = opaque;
+ dcr->dcr_read = dcr_read;
+ dcr->dcr_write = dcr_write;
+
+ return 0;
+}
+
+int ppc_dcr_init (CPUState *env, int (*read_error)(int dcrn),
+ int (*write_error)(int dcrn))
+{
+ ppc_dcr_t *dcr_env;
+
+ dcr_env = qemu_mallocz(sizeof(ppc_dcr_t));
+ if (dcr_env == NULL)
+ return -1;
+ dcr_env->read_error = read_error;
+ dcr_env->write_error = write_error;
+ env->dcr_env = dcr_env;
+
+ return 0;