Signed-off-by: Jean-Christophe Dubois <j...@tribudubois.net>
---
Changes since v1:
* rework loging to match other i.MX drivers
Changes since v2:
* We moved to an inheritance QOM scheme
hw/arm/fsl-imx25.c | 2 +-
hw/misc/Makefile.objs | 1 +
hw/misc/imx25_ccm.c | 243 ++++++++++++++++++++++++++++++++++++++++++++
include/hw/arm/fsl-imx25.h | 4 +-
include/hw/misc/imx25_ccm.h | 59 +++++++++++
5 files changed, 306 insertions(+), 3 deletions(-)
create mode 100644 hw/misc/imx25_ccm.c
create mode 100644 include/hw/misc/imx25_ccm.h
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 5526c22..0aacc91 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_IMX31_CCM);
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c77f3e3..8a235df 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -27,6 +27,7 @@ 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_IMX) += imx25_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/imx25_ccm.c b/hw/misc/imx25_ccm.c
new file mode 100644
index 0000000..a6c9bff
--- /dev/null
+++ b/hw/misc/imx25_ccm.c
@@ -0,0 +1,243 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <j...@tribudubois.net>
+ *
+ * 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.
+ */
+
+#include "hw/misc/imx25_ccm.h"
+
+#ifndef DEBUG_IMX25_CCM
+#define DEBUG_IMX25_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX25_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx25_ccm = {
+ .name = TYPE_IMX25_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(mpctl, IMX25CCMState),
+ VMSTATE_UINT32(upctl, IMX25CCMState),
+ VMSTATE_UINT32(cctl, IMX25CCMState),
+ VMSTATE_UINT32_ARRAY(cgcr, IMX25CCMState, 3),
+ VMSTATE_UINT32_ARRAY(pcdr, IMX25CCMState, 4),
+ VMSTATE_UINT32(rcsr, IMX25CCMState),
+ VMSTATE_UINT32(crdr, IMX25CCMState),
+ VMSTATE_UINT32_ARRAY(dcvr, IMX25CCMState, 4),
+ VMSTATE_UINT32_ARRAY(ltr, IMX25CCMState, 4),
+ VMSTATE_UINT32_ARRAY(ltbr, IMX25CCMState, 2),
+ VMSTATE_UINT32_ARRAY(pmcr, IMX25CCMState, 3),
+ VMSTATE_UINT32(mcr, IMX25CCMState),
+ VMSTATE_UINT32_ARRAY(lpimr, IMX25CCMState, 2),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("()\n");
+
+ if (EXTRACT(s->cctl, MPLL_BYPASS)) {
+ return CKIH_FREQ;
+ } else {
+ return imx_ccm_calc_pll(s->mpctl, CKIH_FREQ);
+ }
+}
+
+static uint32_t imx25_ccm_get_upll_clk(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("()\n");
+
+ return imx_ccm_calc_pll(s->upctl, CKIH_FREQ);
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("()\n");
+
+ if (EXTRACT(s->cctl, ARM_SRC)) {
+ return (imx25_ccm_get_mpll_clk(dev) * 3 / 4) /
+ (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+ } else {
+ return imx25_ccm_get_mpll_clk(dev) /
+ (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+ }
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("()\n");
+
+ return imx25_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->cctl, AHB_CLK_DIV));
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(DeviceState *dev)
+{
+ DPRINTF("()\n");
+
+ return imx25_ccm_get_ahb_clk(dev) / 2;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+ DPRINTF("Clock = %d)\n", clock);