From 9de46a0aa3aa0d1db019dc5e2152a2bbf484180d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Thu, 17 Dec 2015 13:37:15 +0000 Subject: i.MX: Fix i.MX31 default/reset configuration Linux on i.MX31/KZM is expecting the CCM to use the CKIH ref clock instead of the CKIL plus the FPM multiplier. We change the CCMR reg reset value to match linux expected config. This allows the CCM to provide a 39MHz clk (as expected by linux) instead of the actual 50MHz. With this change the "sleep 60" command on linux is time accurate with "real world time". Signed-off-by: Jean-Christophe Dubois Reviewed-by: Peter Crosthwaite Message-id: 6dc5bc4e0a450b20cecdb2991112e7281b653345.1449528242.git.jcd@tribudubois.net Signed-off-by: Peter Maydell --- hw/misc/imx_ccm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/misc/imx_ccm.c') diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c index 4cc2bbc0ed..500dda5024 100644 --- a/hw/misc/imx_ccm.c +++ b/hw/misc/imx_ccm.c @@ -123,7 +123,7 @@ static void imx_ccm_reset(DeviceState *dev) { IMXCCMState *s = IMX_CCM(dev); - s->ccmr = 0x074b0b7b; + s->ccmr = 0x074b0b7d; s->pdr0 = 0xff870b48; s->pdr1 = 0x49fcfe7f; s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0); -- cgit 1.4.1 From aaa9ec3b4ddaea53c6218ac90bf4ebc4b0bcff31 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Thu, 17 Dec 2015 13:37:15 +0000 Subject: i.MX: rename i.MX CCM get_clock() function and CLK ID enum names This is to prepare for CCM code refactoring. This is just a bit of function and enum values renaming. We also remove some useless intermediate variables. Signed-off-by: Jean-Christophe Dubois Reviewed-by: Peter Crosthwaite Message-id: 53c4d9b9611988a5f56f178f285e04490747925e.1449528242.git.jcd@tribudubois.net Signed-off-by: Peter Maydell --- hw/misc/imx_ccm.c | 8 ++++---- hw/timer/imx_epit.c | 20 +++++++++----------- hw/timer/imx_gpt.c | 16 ++++++++-------- include/hw/misc/imx_ccm.h | 8 ++++---- 4 files changed, 25 insertions(+), 27 deletions(-) (limited to 'hw/misc/imx_ccm.c') diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c index 500dda5024..1ac697a998 100644 --- a/hw/misc/imx_ccm.c +++ b/hw/misc/imx_ccm.c @@ -49,18 +49,18 @@ static const VMStateDescription vmstate_imx_ccm = { .post_load = imx_ccm_post_load, }; -uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock) +uint32_t imx_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock) { IMXCCMState *s = IMX_CCM(dev); switch (clock) { case NOCLK: return 0; - case MCU: + case CLK_MCU: return s->mcu_clk_freq; - case HSP: + case CLK_HSP: return s->hsp_clk_freq; - case IPG: + case CLK_IPG: return s->ipg_clk_freq; case CLK_32k: return CKIL_FREQ; diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index 967be4ad27..50bf83c253 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -51,9 +51,9 @@ static char const *imx_epit_reg_name(uint32_t reg) * These are typical. */ static const IMXClk imx_epit_clocks[] = { - 0, /* 00 disabled */ - IPG, /* 01 ipg_clk, ~532MHz */ - IPG, /* 10 ipg_clk_highfreq */ + NOCLK, /* 00 disabled */ + CLK_IPG, /* 01 ipg_clk, ~532MHz */ + CLK_IPG, /* 10 ipg_clk_highfreq */ CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ }; @@ -73,20 +73,18 @@ static void imx_epit_set_freq(IMXEPITState *s) { uint32_t clksrc; uint32_t prescaler; - uint32_t freq; clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2); prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12); - freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler; + s->freq = imx_ccm_get_clock_frequency(s->ccm, + imx_epit_clocks[clksrc]) / prescaler; - s->freq = freq; + DPRINTF("Setting ptimer frequency to %u\n", s->freq); - DPRINTF("Setting ptimer frequency to %u\n", freq); - - if (freq) { - ptimer_set_freq(s->timer_reload, freq); - ptimer_set_freq(s->timer_cmp, freq); + if (s->freq) { + ptimer_set_freq(s->timer_reload, s->freq); + ptimer_set_freq(s->timer_cmp, s->freq); } } diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 7257f4201a..b1893b8350 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -81,8 +81,8 @@ static const VMStateDescription vmstate_imx_timer_gpt = { static const IMXClk imx_gpt_clocks[] = { NOCLK, /* 000 No clock source */ - IPG, /* 001 ipg_clk, 532MHz*/ - IPG, /* 010 ipg_clk_highfreq */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG, /* 010 ipg_clk_highfreq */ NOCLK, /* 011 not defined */ CLK_32k, /* 100 ipg_clk_32k */ NOCLK, /* 101 not defined */ @@ -93,14 +93,14 @@ static const IMXClk imx_gpt_clocks[] = { static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); - uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc]) - / (1 + s->pr); - s->freq = freq; - DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq); + s->freq = imx_ccm_get_clock_frequency(s->ccm, + imx_gpt_clocks[clksrc]) / (1 + s->pr); - if (freq) { - ptimer_set_freq(s->timer, freq); + DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); + + if (s->freq) { + ptimer_set_freq(s->timer, s->freq); } } diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h index 0f2e469b23..09f6248053 100644 --- a/include/hw/misc/imx_ccm.h +++ b/include/hw/misc/imx_ccm.h @@ -80,12 +80,12 @@ typedef struct IMXCCMState { typedef enum { NOCLK, - MCU, - HSP, - IPG, + CLK_MCU, + CLK_HSP, + CLK_IPG, CLK_32k } IMXClk; -uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock); +uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock); #endif /* IMX_CCM_H */ -- cgit 1.4.1 From cb54d868c6a2292443645f25b295630f925474f8 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Thu, 17 Dec 2015 13:37:15 +0000 Subject: i.MX: Split the CCM class into an abstract base class and a concrete class The IMX_CCM class is now the base abstract class that is used by EPIT and GPT timer implementation. IMX31_CCM class is the concrete class implementing CCM for i.MX31 SOC. For now the i.MX25 continues to use the i.MX31 CCM implementation. An i.MX25 specific CCM will be introduced in a later patch. We also rework initialization to stop using deprecated sysbus device init. Signed-off-by: Jean-Christophe Dubois Reviewed-by: Peter Crosthwaite Message-id: fd3c7f87b50f5ebc99ec91f01413db35017f116d.1449528242.git.jcd@tribudubois.net Signed-off-by: Peter Maydell --- hw/arm/fsl-imx25.c | 6 +- hw/arm/fsl-imx31.c | 6 +- hw/misc/Makefile.objs | 1 + hw/misc/imx31_ccm.c | 392 ++++++++++++++++++++++++++++++++++++++++++++ hw/misc/imx_ccm.c | 231 +++----------------------- include/hw/arm/fsl-imx25.h | 4 +- include/hw/arm/fsl-imx31.h | 4 +- include/hw/misc/imx31_ccm.h | 66 ++++++++ include/hw/misc/imx_ccm.h | 69 +++----- include/hw/timer/imx_epit.h | 5 +- include/hw/timer/imx_gpt.h | 5 +- 11 files changed, 521 insertions(+), 268 deletions(-) create mode 100644 hw/misc/imx31_ccm.c create mode 100644 include/hw/misc/imx31_ccm.h (limited to 'hw/misc/imx_ccm.c') diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index e1cadac997..9f302ed580 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj) object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM); + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { @@ -150,7 +150,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ } }; - s->gpt[i].ccm = DEVICE(&s->ccm); + s->gpt[i].ccm = IMX_CCM(&s->ccm); object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err); if (err) { @@ -173,7 +173,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ } }; - s->epit[i].ccm = DEVICE(&s->ccm); + s->epit[i].ccm = IMX_CCM(&s->ccm); object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 53d4473250..abdea0683a 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -35,7 +35,7 @@ static void fsl_imx31_init(Object *obj) object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM); + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { @@ -128,7 +128,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) serial_table[i].irq)); } - s->gpt.ccm = DEVICE(&s->ccm); + s->gpt.ccm = IMX_CCM(&s->ccm); object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err); if (err) { @@ -150,7 +150,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ }, }; - s->epit[i].ccm = DEVICE(&s->ccm); + s->epit[i].ccm = IMX_CCM(&s->ccm); object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); if (err) { diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index aeb6b7da74..c77f3e3049 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -26,6 +26,7 @@ obj-$(CONFIG_NSERIES) += cbus.o obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o obj-$(CONFIG_IMX) += imx_ccm.o +obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c new file mode 100644 index 0000000000..b92d2e0392 --- /dev/null +++ b/hw/misc/imx31_ccm.c @@ -0,0 +1,392 @@ +/* + * IMX31 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * To get the timer frequencies right, we need to emulate at least part of + * the i.MX31 CCM. + */ + +#include "hw/misc/imx31_ccm.h" + +#define CKIH_FREQ 26000000 /* 26MHz crystal input */ + +#ifndef DEBUG_IMX31_CCM +#define DEBUG_IMX31_CCM 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX31_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx31_ccm_reg_name(uint32_t reg) +{ + switch (reg) { + case 0: + return "CCMR"; + case 1: + return "PDR0"; + case 2: + return "PDR1"; + case 3: + return "RCSR"; + case 4: + return "MPCTL"; + case 5: + return "UPCTL"; + case 6: + return "SPCTL"; + case 7: + return "COSR"; + case 8: + return "CGR0"; + case 9: + return "CGR1"; + case 10: + return "CGR2"; + case 11: + return "WIMR"; + case 12: + return "LDC"; + case 13: + return "DCVR0"; + case 14: + return "DCVR1"; + case 15: + return "DCVR2"; + case 16: + return "DCVR3"; + case 17: + return "LTR0"; + case 18: + return "LTR1"; + case 19: + return "LTR2"; + case 20: + return "LTR3"; + case 21: + return "LTBR0"; + case 22: + return "LTBR1"; + case 23: + return "PMCR0"; + case 24: + return "PMCR1"; + case 25: + return "PDR2"; + default: + return "???"; + } +} + +static const VMStateDescription vmstate_imx31_ccm = { + .name = TYPE_IMX31_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ccmr, IMX31CCMState), + VMSTATE_UINT32(pdr0, IMX31CCMState), + VMSTATE_UINT32(pdr1, IMX31CCMState), + VMSTATE_UINT32(mpctl, IMX31CCMState), + VMSTATE_UINT32(spctl, IMX31CCMState), + VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3), + VMSTATE_UINT32(pmcr0, IMX31CCMState), + VMSTATE_UINT32(pmcr1, IMX31CCMState), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev) +{ + uint32_t freq = 0; + IMX31CCMState *s = IMX31_CCM(dev); + + if ((s->ccmr & CCMR_PRCS) == 2) { + if (s->ccmr & CCMR_FPME) { + freq = CKIL_FREQ; + if (s->ccmr & CCMR_FPMF) { + freq *= 1024; + } + } + } else { + freq = CKIH_FREQ; + } + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) { + freq = imx31_ccm_get_pll_ref_clk(dev); + } else { + freq = imx31_ccm_get_mpll_clk(dev); + } + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MCU)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, HSP)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev) +{ + uint32_t freq; + IMX31CCMState *s = IMX31_CCM(dev); + + freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG)); + + DPRINTF("freq = %d\n", freq); + + return freq; +} + +static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + + switch (clock) { + case NOCLK: + break; + case CLK_MCU: + freq = imx31_ccm_get_mcu_clk(dev); + break; + case CLK_HSP: + freq = imx31_ccm_get_hsp_clk(dev); + break; + case CLK_IPG: + freq = imx31_ccm_get_ipg_clk(dev); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX31_CCM, __func__, clock); + break; + } + + DPRINTF("Clock = %d) = %d\n", clock, freq); + + return freq; +} + +static void imx31_ccm_reset(DeviceState *dev) +{ + IMX31CCMState *s = IMX31_CCM(dev); + + DPRINTF("()\n"); + + s->ccmr = 0x074b0b7d; + s->pdr0 = 0xff870b48; + s->pdr1 = 0x49fcfe7f; + s->mpctl = 0x04001800; + s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff; + s->spctl = 0x04043001; + s->pmcr0 = 0x80209828; + s->pmcr1 = 0x00aa0000; +} + +static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32 value = 0; + IMX31CCMState *s = (IMX31CCMState *)opaque; + + switch (offset >> 2) { + case 0: /* CCMR */ + value = s->ccmr; + break; + case 1: + value = s->pdr0; + break; + case 2: + value = s->pdr1; + break; + case 4: + value = s->mpctl; + break; + case 6: + value = s->spctl; + break; + case 8: + value = s->cgr[0]; + break; + case 9: + value = s->cgr[1]; + break; + case 10: + value = s->cgr[2]; + break; + case 18: /* LTR1 */ + value = 0x00004040; + break; + case 23: + value = s->pmcr0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset); + break; + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2), + value); + + return (uint64_t)value; +} + +static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX31CCMState *s = (IMX31CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2), + (uint32_t)value); + + switch (offset >> 2) { + case 0: + s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff); + break; + case 1: + s->pdr0 = value & 0xff9f3fff; + break; + case 2: + s->pdr1 = value; + break; + case 4: + s->mpctl = value & 0xbfff3fff; + break; + case 6: + s->spctl = value & 0xbfff3fff; + break; + case 8: + s->cgr[0] = value; + break; + case 9: + s->cgr[1] = value; + break; + case 10: + s->cgr[2] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset); + break; + } +} + +static const struct MemoryRegionOps imx31_ccm_ops = { + .read = imx31_ccm_read, + .write = imx31_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + +}; + +static void imx31_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX31CCMState *s = IMX31_CCM(obj); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s, + TYPE_IMX31_CCM, 0x1000); + sysbus_init_mmio(sd, &s->iomem); +} + +static void imx31_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx31_ccm_reset; + dc->vmsd = &vmstate_imx31_ccm; + dc->desc = "i.MX31 Clock Control Module"; + + ccm->get_clock_frequency = imx31_ccm_get_clock_frequency; +} + +static const TypeInfo imx31_ccm_info = { + .name = TYPE_IMX31_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX31CCMState), + .instance_init = imx31_ccm_init, + .class_init = imx31_ccm_class_init, +}; + +static void imx31_ccm_register_types(void) +{ + type_register_static(&imx31_ccm_info); +} + +type_init(imx31_ccm_register_types) diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c index 1ac697a998..0c9740bd7a 100644 --- a/hw/misc/imx_ccm.c +++ b/hw/misc/imx_ccm.c @@ -7,15 +7,12 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * - * To get the timer frequencies right, we need to emulate at least part of - * the CCM. + * This is an abstract base class used to get a common interface to + * retrieve the CCM frequencies from the various i.MX SOC. */ #include "hw/misc/imx_ccm.h" -#define CKIH_FREQ 26000000 /* 26MHz crystal input */ -#define CKIL_FREQ 32768 /* nominal 32khz clock */ - #ifndef DEBUG_IMX_CCM #define DEBUG_IMX_CCM 0 #endif @@ -28,51 +25,27 @@ } \ } while (0) -static int imx_ccm_post_load(void *opaque, int version_id); - -static const VMStateDescription vmstate_imx_ccm = { - .name = TYPE_IMX_CCM, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ccmr, IMXCCMState), - VMSTATE_UINT32(pdr0, IMXCCMState), - VMSTATE_UINT32(pdr1, IMXCCMState), - VMSTATE_UINT32(mpctl, IMXCCMState), - VMSTATE_UINT32(spctl, IMXCCMState), - VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3), - VMSTATE_UINT32(pmcr0, IMXCCMState), - VMSTATE_UINT32(pmcr1, IMXCCMState), - VMSTATE_UINT32(pll_refclk_freq, IMXCCMState), - VMSTATE_END_OF_LIST() - }, - .post_load = imx_ccm_post_load, -}; -uint32_t imx_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock) +uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) { - IMXCCMState *s = IMX_CCM(dev); + uint32_t freq = 0; + IMXCCMClass *klass = IMX_GET_CLASS(dev); - switch (clock) { - case NOCLK: - return 0; - case CLK_MCU: - return s->mcu_clk_freq; - case CLK_HSP: - return s->hsp_clk_freq; - case CLK_IPG: - return s->ipg_clk_freq; - case CLK_32k: - return CKIL_FREQ; + if (klass->get_clock_frequency) { + freq = klass->get_clock_frequency(dev, clock); } - return 0; + + DPRINTF("(clock = %d) = %d\n", clock, freq); + + return freq; } /* * Calculate PLL output frequency */ -static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq) +uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq) { + int32_t freq; int32_t mfn = MFN(pllreg); /* Numerator */ uint32_t mfi = MFI(pllreg); /* Integer part */ uint32_t mfd = 1 + MFD(pllreg); /* Denominator */ @@ -81,186 +54,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq) if (mfi < 5) { mfi = 5; } + /* mfn is 10-bit signed twos-complement */ mfn <<= 32 - 10; mfn >>= 32 - 10; - return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / + freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / (mfd * pd)) << 10; -} - -static void update_clocks(IMXCCMState *s) -{ - /* - * If we ever emulate more clocks, this should switch to a data-driven - * approach - */ - - if ((s->ccmr & CCMR_PRCS) == 2) { - s->pll_refclk_freq = CKIL_FREQ * 1024; - } else { - s->pll_refclk_freq = CKIH_FREQ; - } - - /* ipg_clk_arm aka MCU clock */ - if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) { - s->mcu_clk_freq = s->pll_refclk_freq; - } else { - s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq); - } - - /* High-speed clock */ - s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP)); - s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG)); - - DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n", - s->mcu_clk_freq / 1000000, - s->hsp_clk_freq / 1000000, - s->ipg_clk_freq); -} - -static void imx_ccm_reset(DeviceState *dev) -{ - IMXCCMState *s = IMX_CCM(dev); - - s->ccmr = 0x074b0b7d; - s->pdr0 = 0xff870b48; - s->pdr1 = 0x49fcfe7f; - s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0); - s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff; - s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1); - s->pmcr0 = 0x80209828; - - update_clocks(s); -} - -static uint64_t imx_ccm_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset); - - switch (offset >> 2) { - case 0: /* CCMR */ - DPRINTF(" ccmr = 0x%x\n", s->ccmr); - return s->ccmr; - case 1: - DPRINTF(" pdr0 = 0x%x\n", s->pdr0); - return s->pdr0; - case 2: - DPRINTF(" pdr1 = 0x%x\n", s->pdr1); - return s->pdr1; - case 4: - DPRINTF(" mpctl = 0x%x\n", s->mpctl); - return s->mpctl; - case 6: - DPRINTF(" spctl = 0x%x\n", s->spctl); - return s->spctl; - case 8: - DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]); - return s->cgr[0]; - case 9: - DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]); - return s->cgr[1]; - case 10: - DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]); - return s->cgr[2]; - case 18: /* LTR1 */ - return 0x00004040; - case 23: - DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0); - return s->pmcr0; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset); - return 0; - } -} - -static void imx_ccm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - - DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n", - offset, (unsigned int)value); - - switch (offset >> 2) { - case 0: - s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff); - break; - case 1: - s->pdr0 = value & 0xff9f3fff; - break; - case 2: - s->pdr1 = value; - break; - case 4: - s->mpctl = value & 0xbfff3fff; - break; - case 6: - s->spctl = value & 0xbfff3fff; - break; - case 8: - s->cgr[0] = value; - return; - case 9: - s->cgr[1] = value; - return; - case 10: - s->cgr[2] = value; - return; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset); - return; - } - update_clocks(s); -} - -static const struct MemoryRegionOps imx_ccm_ops = { - .read = imx_ccm_read, - .write = imx_ccm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int imx_ccm_init(SysBusDevice *dev) -{ - IMXCCMState *s = IMX_CCM(dev); - - memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s, - TYPE_IMX_CCM, 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static int imx_ccm_post_load(void *opaque, int version_id) -{ - IMXCCMState *s = (IMXCCMState *)opaque; - - update_clocks(s); - return 0; -} - -static void imx_ccm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq, + freq); - sbc->init = imx_ccm_init; - dc->reset = imx_ccm_reset; - dc->vmsd = &vmstate_imx_ccm; - dc->desc = "i.MX Clock Control Module"; + return freq; } static const TypeInfo imx_ccm_info = { - .name = TYPE_IMX_CCM, - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_IMX_CCM, + .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXCCMState), - .class_init = imx_ccm_class_init, + .class_size = sizeof(IMXCCMClass), + .abstract = true, }; static void imx_ccm_register_types(void) diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 73f50c64d8..5c62fde907 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -19,7 +19,7 @@ #include "hw/arm/arm.h" #include "hw/intc/imx_avic.h" -#include "hw/misc/imx_ccm.h" +#include "hw/misc/imx31_ccm.h" #include "hw/char/imx_serial.h" #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" @@ -44,7 +44,7 @@ typedef struct FslIMX25State { /*< public >*/ ARMCPU cpu; IMXAVICState avic; - IMXCCMState ccm; + IMX31CCMState ccm; IMXSerialState uart[FSL_IMX25_NUM_UARTS]; IMXGPTState gpt[FSL_IMX25_NUM_GPTS]; IMXEPITState epit[FSL_IMX25_NUM_EPITS]; diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index 5e8f795f98..d408abbba0 100644 --- a/include/hw/arm/fsl-imx31.h +++ b/include/hw/arm/fsl-imx31.h @@ -19,7 +19,7 @@ #include "hw/arm/arm.h" #include "hw/intc/imx_avic.h" -#include "hw/misc/imx_ccm.h" +#include "hw/misc/imx31_ccm.h" #include "hw/char/imx_serial.h" #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" @@ -42,7 +42,7 @@ typedef struct FslIMX31State { /*< public >*/ ARMCPU cpu; IMXAVICState avic; - IMXCCMState ccm; + IMX31CCMState ccm; IMXSerialState uart[FSL_IMX31_NUM_UARTS]; IMXGPTState gpt; IMXEPITState epit[FSL_IMX31_NUM_EPITS]; diff --git a/include/hw/misc/imx31_ccm.h b/include/hw/misc/imx31_ccm.h new file mode 100644 index 0000000000..fcae36d426 --- /dev/null +++ b/include/hw/misc/imx31_ccm.h @@ -0,0 +1,66 @@ +/* + * IMX31 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX31_CCM_H +#define IMX31_CCM_H + +#include "hw/misc/imx_ccm.h" + +/* CCMR */ +#define CCMR_FPME (1<<0) +#define CCMR_MPE (1<<3) +#define CCMR_MDS (1<<7) +#define CCMR_FPMF (1<<26) +#define CCMR_PRCS (3<<1) + +#define PMCR0_DFSUP1 (1<<31) + +/* PDR0 */ +#define PDR0_MCU_PODF_SHIFT (0) +#define PDR0_MCU_PODF_MASK (0x7) +#define PDR0_MAX_PODF_SHIFT (3) +#define PDR0_MAX_PODF_MASK (0x7) +#define PDR0_IPG_PODF_SHIFT (6) +#define PDR0_IPG_PODF_MASK (0x3) +#define PDR0_NFC_PODF_SHIFT (8) +#define PDR0_NFC_PODF_MASK (0x7) +#define PDR0_HSP_PODF_SHIFT (11) +#define PDR0_HSP_PODF_MASK (0x7) +#define PDR0_PER_PODF_SHIFT (16) +#define PDR0_PER_PODF_MASK (0x1f) +#define PDR0_CSI_PODF_SHIFT (23) +#define PDR0_CSI_PODF_MASK (0x1ff) + +#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \ + & PDR0_##name##_PODF_MASK) +#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \ + PDR0_##name##_PODF_SHIFT) + +#define TYPE_IMX31_CCM "imx31.ccm" +#define IMX31_CCM(obj) OBJECT_CHECK(IMX31CCMState, (obj), TYPE_IMX31_CCM) + +typedef struct IMX31CCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t ccmr; + uint32_t pdr0; + uint32_t pdr1; + uint32_t mpctl; + uint32_t spctl; + uint32_t cgr[3]; + uint32_t pmcr0; + uint32_t pmcr1; +} IMX31CCMState; + +#endif /* IMX31_CCM_H */ diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h index 09f6248053..5c4b7958bd 100644 --- a/include/hw/misc/imx_ccm.h +++ b/include/hw/misc/imx_ccm.h @@ -1,5 +1,5 @@ /* - * IMX31 Clock Control Module + * IMX Clock Control Module base class * * Copyright (C) 2012 NICTA * Updated by Jean-Christophe Dubois @@ -13,33 +13,7 @@ #include "hw/sysbus.h" -/* CCMR */ -#define CCMR_FPME (1<<0) -#define CCMR_MPE (1<<3) -#define CCMR_MDS (1<<7) -#define CCMR_FPMF (1<<26) -#define CCMR_PRCS (3<<1) - -/* PDR0 */ -#define PDR0_MCU_PODF_SHIFT (0) -#define PDR0_MCU_PODF_MASK (0x7) -#define PDR0_MAX_PODF_SHIFT (3) -#define PDR0_MAX_PODF_MASK (0x7) -#define PDR0_IPG_PODF_SHIFT (6) -#define PDR0_IPG_PODF_MASK (0x3) -#define PDR0_NFC_PODF_SHIFT (8) -#define PDR0_NFC_PODF_MASK (0x7) -#define PDR0_HSP_PODF_SHIFT (11) -#define PDR0_HSP_PODF_MASK (0x7) -#define PDR0_PER_PODF_SHIFT (16) -#define PDR0_PER_PODF_MASK (0x1f) -#define PDR0_CSI_PODF_SHIFT (23) -#define PDR0_CSI_PODF_MASK (0x1ff) - -#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \ - & PDR0_##name##_PODF_MASK) -#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \ - PDR0_##name##_PODF_SHIFT) +#define CKIL_FREQ 32768 /* nominal 32khz clock */ /* PLL control registers */ #define PD(v) (((v) >> 26) & 0xf) @@ -53,39 +27,44 @@ #define PLL_MFN(x) (((x) & 0x3ff) << 0) #define TYPE_IMX_CCM "imx.ccm" -#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM) +#define IMX_CCM(obj) \ + OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM) +#define IMX_CCM_CLASS(klass) \ + OBJECT_CLASS_CHECK(IMXCCMClass, (klass), TYPE_IMX_CCM) +#define IMX_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IMXCCMClass, (obj), TYPE_IMX_CCM) typedef struct IMXCCMState { /* */ SysBusDevice parent_obj; /* */ - MemoryRegion iomem; - uint32_t ccmr; - uint32_t pdr0; - uint32_t pdr1; - uint32_t mpctl; - uint32_t spctl; - uint32_t cgr[3]; - uint32_t pmcr0; - uint32_t pmcr1; - - /* Frequencies precalculated on register changes */ - uint32_t pll_refclk_freq; - uint32_t mcu_clk_freq; - uint32_t hsp_clk_freq; - uint32_t ipg_clk_freq; } IMXCCMState; typedef enum { NOCLK, + CLK_MPLL, + CLK_UPLL, CLK_MCU, CLK_HSP, + CLK_MAX, + CLK_AHB, CLK_IPG, + CLK_PER, CLK_32k } IMXClk; -uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock); +typedef struct IMXCCMClass { + /* */ + SysBusDeviceClass parent_class; + + /* */ + uint32_t (*get_clock_frequency)(IMXCCMState *s, IMXClk clk); +} IMXCCMClass; + +uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq); + +uint32_t imx_ccm_get_clock_frequency(IMXCCMState *s, IMXClk clock); #endif /* IMX_CCM_H */ diff --git a/include/hw/timer/imx_epit.h b/include/hw/timer/imx_epit.h index c5328aefe1..0730ac35e6 100644 --- a/include/hw/timer/imx_epit.h +++ b/include/hw/timer/imx_epit.h @@ -31,6 +31,7 @@ #include "hw/sysbus.h" #include "hw/ptimer.h" +#include "hw/misc/imx_ccm.h" /* * EPIT: Enhanced periodic interrupt timer @@ -63,8 +64,8 @@ typedef struct IMXEPITState{ /*< public >*/ ptimer_state *timer_reload; ptimer_state *timer_cmp; - MemoryRegion iomem; - DeviceState *ccm; + MemoryRegion iomem; + IMXCCMState *ccm; uint32_t cr; uint32_t sr; diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index 3f02d3b337..461adbe53f 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -31,6 +31,7 @@ #include "hw/sysbus.h" #include "hw/ptimer.h" +#include "hw/misc/imx_ccm.h" /* * GPT : General purpose timer @@ -82,8 +83,8 @@ typedef struct IMXGPTState{ /*< public >*/ ptimer_state *timer; - MemoryRegion iomem; - DeviceState *ccm; + MemoryRegion iomem; + IMXCCMState *ccm; uint32_t cr; uint32_t pr; -- cgit 1.4.1