From f1733ebc2af65fce61b0fb93006b1ccb091fefe2 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 25 Mar 2009 21:04:46 +0000 Subject: [PATCH] S3C: Added support for changeable xtal speed The new init parameter allows the vqrious IO blocks to use the "true" clock speeds. This patch remove the need for a constant for HCLK as it's derivated from the clock registers. Signed-off-by: Michel Pollet --- hw/mini2440.c | 2 +- hw/s3c.h | 23 ++++++++++++++------ hw/s3c2410.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/hw/mini2440.c b/hw/mini2440.c index 1fb3991f1b..2dcce43294 100644 --- a/hw/mini2440.c +++ b/hw/mini2440.c @@ -298,7 +298,7 @@ static struct mini2440_board_s *mini2440_init_common(int ram_size, fprintf(stderr, "This platform requires an ARM920T core\n"); exit(2); } - s->cpu = s3c24xx_init(S3C_CPU_2440, s->ram, S3C_SRAM_BASE_NANDBOOT, s->mmc); + s->cpu = s3c24xx_init(S3C_CPU_2440, 12000000 /* 12 mhz */, s->ram, S3C_SRAM_BASE_NANDBOOT, s->mmc); /* Setup peripherals */ mini2440_gpio_setup(s); diff --git a/hw/s3c.h b/hw/s3c.h index 5afbb314b2..d76acf26af 100644 --- a/hw/s3c.h +++ b/hw/s3c.h @@ -103,7 +103,6 @@ # define S3C_SRAM_BASE_NANDBOOT 0x00000000 # define S3C_SRAM_SIZE 0x00001000 -# define S3C_PCLK_FREQ 66500000 /* Hz */ # define S3C_XTAL_FREQ 32768 /* Hz */ /* s3c2410.c */ @@ -119,14 +118,16 @@ qemu_irq *s3c_dma_get(struct s3c_dma_state_s *s); /* GPIO TODO: remove this out, replace with qemu_irq or sumpthin */ typedef void (*gpio_handler_t)(int line, int level, void *opaque); +struct s3c_freq_s; struct s3c_timers_state_s; -struct s3c_timers_state_s *s3c_timers_init(target_phys_addr_t base, - qemu_irq *pic, qemu_irq *dma); +struct s3c_timers_state_s *s3c_timers_init(struct s3c_freq_s * freq, + target_phys_addr_t base, qemu_irq *pic, qemu_irq *dma); void s3c_timers_cmp_handler_set(void *opaque, int line, gpio_handler_t handler, void *cmp_opaque); +struct s3c_freq_s; struct s3c_uart_state_s; -struct s3c_uart_state_s *s3c_uart_init(target_phys_addr_t base, +struct s3c_uart_state_s *s3c_uart_init(struct s3c_freq_s * freq, target_phys_addr_t base, qemu_irq *irqs, qemu_irq *dma); void s3c_uart_attach(struct s3c_uart_state_s *s, CharDriverState *chr); @@ -142,8 +143,9 @@ i2c_bus *s3c_i2c_bus(struct s3c_i2c_state_s *s); struct s3c_i2s_state_s; struct s3c_i2s_state_s *s3c_i2s_init(target_phys_addr_t base, qemu_irq *dma); +struct s3c_freq_s; struct s3c_wdt_state_s; -struct s3c_wdt_state_s *s3c_wdt_init(target_phys_addr_t base, qemu_irq irq); +struct s3c_wdt_state_s *s3c_wdt_init(struct s3c_freq_s * freq, target_phys_addr_t base, qemu_irq irq); /* s3c24xx_gpio.c */ struct s3c_gpio_state_s; @@ -197,8 +199,17 @@ void s3c_spi_attach(struct s3c_spi_state_s *s, int ch, uint8_t (*txrx)(void *opaque, uint8_t value), uint8_t (*btxrx)(void *opaque, uint8_t value), void *opaque); +struct s3c_freq_s { + uint32_t xtal; /* 16 or 12Mhz : Set in init()*/ + /* These are recalculated when the guest code changes the clock registers */ + uint32_t clk; /* CPU clock */ + uint32_t hclk; /* SDRAM clock */ + uint32_t pclk; /* peripheral clock */ +}; + struct s3c_state_s { CPUState *env; + struct s3c_freq_s clock; uint32_t cpu_id; qemu_irq *irq; qemu_irq *drq; @@ -228,7 +239,7 @@ struct s3c_state_s { }; /* s3c2410.c */ -struct s3c_state_s *s3c24xx_init(uint32_t cpu_id, unsigned int sdram_size, uint32_t sram_address, +struct s3c_state_s *s3c24xx_init(uint32_t cpu_id, uint32_t xtal, unsigned int sdram_size, uint32_t sram_address, SDState *mmc); diff --git a/hw/s3c2410.c b/hw/s3c2410.c index 637f47fc2b..66046b1283 100644 --- a/hw/s3c2410.c +++ b/hw/s3c2410.c @@ -450,6 +450,41 @@ static int s3c_mc_load(QEMUFile *f, void *opaque, int version_id) #define S3C2440_CAMDIVN 0x18 /* Camera Clock Divider register */ +static void s3c_clkpwr_update(struct s3c_state_s *s) +{ + uint32_t mpll = s->clkpwr_regs[S3C_MPLLCON >> 2], + ratio = s->clkpwr_regs[S3C_CLKDIVN >> 2]; + uint32_t mdiv = (mpll >> 12) & 0xff, + pdiv = (mpll >> 4) & 0x3f, + sdiv = (mpll) & 0x3; + + s->clock.clk = ((mdiv + 8) * s->clock.xtal * 2) / + ((pdiv + 2) * (1 << sdiv)); + + switch( (ratio & 0x6) >> 1 ) { + case 0: + s->clock.hclk = s->clock.clk; + break; + case 1: + s->clock.hclk = s->clock.clk/2; + break; + case 2: + s->clock.hclk = s->clock.clk/4; + break; + case 3: + s->clock.hclk = s->clock.clk/3; + break; + } + switch ( ratio&0x1) { + case 0: + s->clock.pclk = s->clock.hclk; + break; + case 1: + s->clock.pclk = s->clock.hclk/2; + break; + } +} + static void s3c_clkpwr_reset(struct s3c_state_s *s) { s->clkpwr_regs[S3C_LOCKTIME >> 2] = 0x00ffffff; @@ -459,6 +494,7 @@ static void s3c_clkpwr_reset(struct s3c_state_s *s) s->clkpwr_regs[S3C_CLKSLOW >> 2] = 0x00000004; s->clkpwr_regs[S3C_CLKDIVN >> 2] = 0x00000000; s->clkpwr_regs[S3C2440_CAMDIVN >> 2] = 0x00000000; + s3c_clkpwr_update(s); } static uint32_t s3c_clkpwr_read(void *opaque, target_phys_addr_t addr) @@ -489,6 +525,7 @@ static void s3c_clkpwr_write(void *opaque, target_phys_addr_t addr, case S3C_UPLLCON: case S3C_CLKDIVN: s->clkpwr_regs[addr >> 2] = value; + s3c_clkpwr_update(s); break; case S3C_CLKCON: if (value & (1 << 3)) { @@ -841,6 +878,7 @@ qemu_irq *s3c_dma_get(struct s3c_dma_state_s *s) /* PWM timers controller */ struct s3c_timer_state_s; struct s3c_timers_state_s { + struct s3c_freq_s * freq; target_phys_addr_t base; qemu_irq *dma; DisplayState *ds; @@ -890,7 +928,7 @@ static void s3c_timers_start(struct s3c_timers_state_s *s, int tm) if (s->timer[tm].running) return; - s->timer[tm].divider = S3C_PCLK_FREQ >> + s->timer[tm].divider = s->freq->pclk >> (((s->config[1] >> (tm * 4)) & 3) + 1); if (tm < 2) s->timer[tm].divider /= ((s->config[0] >> 0) & 0xff) + 1; @@ -1111,13 +1149,14 @@ static int s3c_timers_load(QEMUFile *f, void *opaque, int version_id) return 0; } -struct s3c_timers_state_s *s3c_timers_init(target_phys_addr_t base, +struct s3c_timers_state_s *s3c_timers_init(struct s3c_freq_s * freq, target_phys_addr_t base, qemu_irq *pic, qemu_irq *dma) { int i, iomemtype; struct s3c_timers_state_s *s = (struct s3c_timers_state_s *) qemu_mallocz(sizeof(struct s3c_timers_state_s)); + s->freq = freq; s->base = base; s->dma = dma; @@ -1156,6 +1195,7 @@ void s3c_timers_cmp_handler_set(void *opaque, int line, /* UART */ struct s3c_uart_state_s { + struct s3c_freq_s * freq; target_phys_addr_t base; qemu_irq *irq; qemu_irq *dma; @@ -1248,7 +1288,7 @@ static void s3c_uart_params_update(struct s3c_uart_state_s *s) return; /* XXX Calculate PCLK frequency from clock manager registers */ - ssp.speed = (S3C_PCLK_FREQ >> 4) / (s->brdiv + 1); + ssp.speed = (s->freq->pclk >> 4) / (s->brdiv + 1); switch ((s->lcontrol >> 3) & 7) { case 4: @@ -1476,13 +1516,14 @@ static int s3c_uart_load(QEMUFile *f, void *opaque, int version_id) return 0; } -struct s3c_uart_state_s *s3c_uart_init(target_phys_addr_t base, +struct s3c_uart_state_s *s3c_uart_init(struct s3c_freq_s * freq, target_phys_addr_t base, qemu_irq *irqs, qemu_irq *dma) { int iomemtype; struct s3c_uart_state_s *s = (struct s3c_uart_state_s *) qemu_mallocz(sizeof(struct s3c_uart_state_s)); + s->freq = freq; s->base = base; s->irq = irqs; s->dma = dma; @@ -2497,6 +2538,7 @@ struct s3c_i2s_state_s *s3c_i2s_init(target_phys_addr_t base, qemu_irq *dma) /* Watchdog Timer */ struct s3c_wdt_state_s { + struct s3c_freq_s * freq; target_phys_addr_t base; qemu_irq irq; uint16_t control; @@ -2514,7 +2556,7 @@ static void s3c_wdt_start(struct s3c_wdt_state_s *s) if (enable) { s->timestamp = qemu_get_clock(vm_clock); qemu_mod_timer(s->tm, s->timestamp + muldiv64(divider * s->count, - ticks_per_sec, S3C_PCLK_FREQ)); + ticks_per_sec, s->freq->pclk)); } else qemu_del_timer(s->tm); } @@ -2525,7 +2567,7 @@ static void s3c_wdt_stop(struct s3c_wdt_state_s *s) int divider = prescaler << (((s->control >> 3) & 3) + 4); int diff; - diff = muldiv64(qemu_get_clock(vm_clock) - s->timestamp, S3C_PCLK_FREQ, + diff = muldiv64(qemu_get_clock(vm_clock) - s->timestamp, s->freq->pclk, ticks_per_sec) / divider; s->count -= MIN(s->count, diff); s->timestamp = qemu_get_clock(vm_clock); @@ -2634,12 +2676,13 @@ static int s3c_wdt_load(QEMUFile *f, void *opaque, int version_id) return 0; } -struct s3c_wdt_state_s *s3c_wdt_init(target_phys_addr_t base, qemu_irq irq) +struct s3c_wdt_state_s *s3c_wdt_init(struct s3c_freq_s * freq, target_phys_addr_t base, qemu_irq irq) { int iomemtype; struct s3c_wdt_state_s *s = (struct s3c_wdt_state_s *) qemu_mallocz(sizeof(struct s3c_wdt_state_s)); + s->freq = freq; s->base = base; s->irq = irq; s->tm = qemu_new_timer(vm_clock, s3c_wdt_timeout, s); @@ -2710,6 +2753,7 @@ struct s3c_state_s * g_s3c; /* Initialise an S3C24XX microprocessor. */ struct s3c_state_s *s3c24xx_init( uint32_t cpu_id, + uint32_t xtal, unsigned int sdram_size, uint32_t sram_address, SDState *mmc) @@ -2721,6 +2765,8 @@ struct s3c_state_s *s3c24xx_init( g_s3c = s; s->cpu_id = cpu_id; + s->clock.xtal = xtal; + s->clock.pclk = 66500000; // S3C_PCLK_FREQ; // TEMP s->env = cpu_init("arm920t"); if (!s->env) { @@ -2751,6 +2797,7 @@ struct s3c_state_s *s3c24xx_init( s->clkpwr_base = 0x4c000000; s3c_clkpwr_reset(s); + iomemtype = cpu_register_io_memory(0, s3c_clkpwr_readfn, s3c_clkpwr_writefn, s); cpu_register_physical_memory(s->clkpwr_base, 0xffffff, iomemtype); @@ -2765,18 +2812,19 @@ struct s3c_state_s *s3c24xx_init( s->nand = s3c2410_nand_init(); for (i = 0; s3c2410_uart[i].base; i ++) { - s->uart[i] = s3c_uart_init(s3c2410_uart[i].base, + s->uart[i] = s3c_uart_init(&s->clock, + s3c2410_uart[i].base, &s->irq[s3c2410_uart[i].irq[0]], &s->drq[s3c2410_uart[i].dma[0]]); if (serial_hds[i]) s3c_uart_attach(s->uart[i], serial_hds[i]); } - s->timers = s3c_timers_init(0x51000000, &s->irq[S3C_PIC_TIMER0], s->drq); + s->timers = s3c_timers_init(&s->clock, 0x51000000, &s->irq[S3C_PIC_TIMER0], s->drq); s->udc = s3c_udc_init(0x52000000, s->irq[S3C_PIC_USBD], s->drq); - s->wdt = s3c_wdt_init(0x53000000, s->irq[S3C_PIC_WDT]); + s->wdt = s3c_wdt_init(&s->clock, 0x53000000, s->irq[S3C_PIC_WDT]); s->i2c = s3c_i2c_init(0x54000000, s->irq[S3C_PIC_IIC]); -- 2.11.4.GIT