|
@@ -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)
|