common-obj-$(CONFIG_PTIMER) += ptimer.o
common-obj-$(CONFIG_MAX7310) += max7310.o
common-obj-$(CONFIG_WM8750) += wm8750.o
+common-obj-$(CONFIG_WM8994) += wm8994.o
common-obj-$(CONFIG_TWL92230) += twl92230.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_LM832X) += lm832x.o
obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
+obj-arm-y += exynos4210_rtc.o exynos4210_cmu.o exynos4210_g3d.o
+obj-arm-y += exynos4210_i2c.o exynos4210_i2s.o exynos4210_audio.o
obj-arm-y += arm_l2x0.o
obj-arm-y += arm_mptimer.o a15mpcore.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
CONFIG_SD=y
CONFIG_MAX7310=y
CONFIG_WM8750=y
+CONFIG_WM8994=y
CONFIG_TWL92230=y
CONFIG_TSC2005=y
CONFIG_LM832X=y
#define EXYNOS4210_IRAM_BASE_ADDR 0x02020000
#define EXYNOS4210_IRAM_SIZE 0x00020000 /* 128 KB */
+#define EXYNOS4210_AUDSS_INTMEM_BASE_ADDR 0x03000000
+#define EXYNOS4210_AUDSS_INTMEM_SIZE 0x00039000 /* 228 KB */
+
+#define EXYNOS4210_VPCI_CFG_BASE_ADDR 0xC2000000
+
/* Secondary CPU startup code is in IROM memory */
#define EXYNOS4210_SMP_BOOT_ADDR EXYNOS4210_IROM_BASE_ADDR
#define EXYNOS4210_SMP_BOOT_SIZE 0x1000
#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000
#define EXYNOS4210_L2X0_BASE_ADDR 0x10502000
+#define EXYNOS4210_I2C_NUMBER 9
+
/*
* exynos4210 IRQ subsystem stub definitions.
*/
MemoryRegion dram1_mem;
MemoryRegion boot_secondary;
MemoryRegion bootreg_mem;
+ MemoryRegion audss_intmem;
+ i2c_bus *i2c_if[EXYNOS4210_I2C_NUMBER];
+ BusState *i2s_bus[3];
+ SysBusDevice *vpci_bus;
} Exynos4210State;
void exynos4210_write_secondary(CPUARMState *env,
int ext);
/*
+ * Interface for exynos4210 Clock Management Units (CMUs)
+ */
+typedef enum {
+ UNSPECIFIED_CMU = -1,
+ EXYNOS4210_CMU_LEFTBUS,
+ EXYNOS4210_CMU_RIGHTBUS,
+ EXYNOS4210_CMU_TOP,
+ EXYNOS4210_CMU_DMC,
+ EXYNOS4210_CMU_CPU,
+ EXYNOS4210_CMU_NUMBER
+} Exynos4210Cmu;
+
+typedef enum {
+ UNSPECIFIED_CLOCK,
+ EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+// EXYNOS4210_USB_PHY,
+// EXYNOS4210_USB_HOST_PHY,
+// EXYNOS4210_HDMI_PHY,
+ EXYNOS4210_APLL,
+ EXYNOS4210_MPLL,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_APLL,
+ EXYNOS4210_SCLK_MPLL,
+ EXYNOS4210_ACLK_100,
+ EXYNOS4210_SCLK_UART0,
+ EXYNOS4210_SCLK_UART1,
+ EXYNOS4210_SCLK_UART2,
+ EXYNOS4210_SCLK_UART3,
+ EXYNOS4210_SCLK_UART4,
+ EXYNOS4210_CLOCKS_NUMBER
+} Exynos4210Clock;
+
+typedef void ClockChangeHandler(void *opaque);
+
+DeviceState *exynos4210_cmu_create(target_phys_addr_t addr, Exynos4210Cmu cmu);
+uint64_t exynos4210_cmu_get_rate(Exynos4210Clock clock_id);
+void exynos4210_register_clock_handler(ClockChangeHandler *func,
+ Exynos4210Clock clock_id,
+ void *opaque);
+
+/*
* exynos4210 UART
*/
+
DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
int fifo_size,
int channel,
--- /dev/null
+/*
+ * Samsung exynos4210 Audio driver
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Vorobiov Stanislav <s.vorobiov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "exynos4210_i2s.h"
+#include "wm8994.h"
+
+/* #define DEBUG_EXYNOS4210_AUDIO */
+
+#ifdef DEBUG_EXYNOS4210_AUDIO
+#define DPRINTF(fmt, ...) \
+ do { \
+ fprintf(stdout, "AUDIO: [%s:%d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); \
+ } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define AUDIO_MAX_WORDS (4096 / EXYNOS4210_I2S_WORD_LEN)
+
+typedef struct {
+ Exynos4210I2SSlave i2s;
+
+ DeviceState *wm8994;
+} Exynos4210AudioState;
+
+static void exynos4210_audio_callback(void *opaque, int free_out_bytes)
+{
+ Exynos4210AudioState *s = (Exynos4210AudioState *)opaque;
+ uint8_t buff[AUDIO_MAX_WORDS * EXYNOS4210_I2S_WORD_LEN];
+ int free_out_words = free_out_bytes / EXYNOS4210_I2S_WORD_LEN;
+ uint32_t num_words;
+
+ if (free_out_words <= 0) {
+ return;
+ }
+
+ if (free_out_words > AUDIO_MAX_WORDS) {
+ free_out_words = AUDIO_MAX_WORDS;
+ }
+
+ if (!exynos4210_i2s_dma_enabled(qdev_get_parent_bus(&s->i2s.qdev))) {
+ return;
+ }
+
+ num_words = exynos4210_i2s_dma_get_words_available(
+ qdev_get_parent_bus(&s->i2s.qdev));
+
+ num_words = MIN(num_words, free_out_words);
+
+ exynos4210_i2s_dma_read(qdev_get_parent_bus(&s->i2s.qdev),
+ &buff[0],
+ num_words);
+
+ num_words = wm8994_dac_write(s->wm8994,
+ &buff[0],
+ num_words * EXYNOS4210_I2S_WORD_LEN) / EXYNOS4210_I2S_WORD_LEN;
+
+ exynos4210_i2s_dma_advance(qdev_get_parent_bus(&s->i2s.qdev), num_words);
+}
+
+static void exynos4210_audio_dma_enable(Exynos4210I2SSlave *i2s, bool enable)
+{
+ Exynos4210AudioState *s =
+ FROM_EXYNOS4210_I2S_SLAVE(Exynos4210AudioState, i2s);
+
+ DPRINTF("enter %d\n", enable);
+
+ wm8994_set_active(s->wm8994, enable);
+}
+
+static void exynos4210_audio_reset(DeviceState *dev)
+{
+ DPRINTF("enter\n");
+}
+
+static int exynos4210_audio_init(Exynos4210I2SSlave *i2s)
+{
+ Exynos4210AudioState *s =
+ FROM_EXYNOS4210_I2S_SLAVE(Exynos4210AudioState, i2s);
+
+ wm8994_data_req_set(s->wm8994, exynos4210_audio_callback, s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_exynos4210_audio = {
+ .name = "exynos4210.audio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_EXYNOS4210_I2S_SLAVE(i2s, Exynos4210AudioState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property exynos4210_audio_properties[] = {
+ {
+ .name = "wm8994",
+ .info = &qdev_prop_ptr,
+ .offset = offsetof(Exynos4210AudioState, wm8994),
+ },
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_audio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ Exynos4210I2SSlaveClass *sc = EXYNOS4210_I2S_SLAVE_CLASS(klass);
+
+ sc->init = exynos4210_audio_init;
+ sc->dma_enable = exynos4210_audio_dma_enable;
+ dc->reset = exynos4210_audio_reset;
+ dc->props = exynos4210_audio_properties;
+ dc->vmsd = &vmstate_exynos4210_audio;
+}
+
+static TypeInfo exynos4210_audio_info = {
+ .name = "exynos4210.audio",
+ .parent = TYPE_EXYNOS4210_I2S_SLAVE,
+ .instance_size = sizeof(Exynos4210AudioState),
+ .class_init = exynos4210_audio_class_init,
+};
+
+static void exynos4210_audio_register_types(void)
+{
+ type_register_static(&exynos4210_audio_info);
+}
+
+type_init(exynos4210_audio_register_types)
--- /dev/null
+/*
+ * exynos4210 Clock Management Units (CMUs) Emulation
+ *
+ * Copyright (C) 2011 Samsung Electronics Co Ltd.
+ * Maksim Kozlov, <m.kozlov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysbus.h"
+
+#include "exynos4210.h"
+
+
+
+#define DEBUG_CMU 0
+#define DEBUG_CMU_EXTEND 0
+
+#if DEBUG_CMU || DEBUG_CMU_EXTEND
+
+ #define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+ #define PRINT_DEBUG_SIMPLE(fmt, args...) \
+ do { \
+ fprintf(stderr, fmt, ## args); \
+ } while (0)
+
+#if DEBUG_CMU_EXTEND
+
+ #define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+ #define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do {} while (0)
+#endif /* EXTEND */
+
+#else
+ #define PRINT_DEBUG(fmt, args...) \
+ do {} while (0)
+ #define PRINT_DEBUG_SIMPLE(fmt, args...) \
+ do {} while (0)
+ #define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do {} while (0)
+#endif
+
+#define PRINT_ERROR(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+
+
+/* function blocks */
+#define LEFTBUS_BLK 0x0
+#define RIGHTBUS_BLK 0x0
+#define TOP_BLK 0x10
+#define TOP0_BLK 0x10
+#define TOP1_BLK 0x14
+#define DMC_BLK 0x0
+#define DMC0_BLK 0x0
+#define DMC1_BLK 0x4
+#define CPU_BLK 0x0
+#define CPU0_BLK 0x0
+#define CPU1_BLK 0x4
+
+#define CAM_BLK 0x20
+#define TV_BLK 0x24
+#define MFC_BLK 0x28
+#define G3D_BLK 0x2C
+#define IMAGE_BLK 0x30
+#define LCD0_BLK 0x34
+#define LCD1_BLK 0x38
+#define MAUDIO_BLK 0x3C
+#define FSYS_BLK 0x40
+#define FSYS0_BLK 0x40
+#define FSYS1_BLK 0x44
+#define FSYS2_BLK 0x48
+#define FSYS3_BLK 0x4C
+#define GPS_BLK 0x4C /* CLK_GATE_IP_GPS in CMU_TOP */
+#define PERIL_BLK 0x50
+#define PERIL0_BLK 0x50
+#define PERIL1_BLK 0x54
+#define PERIL2_BLK 0x58
+#define PERIL3_BLK 0x5C
+#define PERIL4_BLK 0x60
+#define PERIR_BLK 0x60 /* CLK_GATE_IP_PERIR in CMU_TOP */
+#define PERIL5_BLK 0x64
+
+#define BLOCK_MASK 0xFF
+
+/* PLLs */
+/* located in CMU_CPU block */
+#define APLL 0x00
+#define MPLL 0x08
+/* located in CMU_TOP block */
+#define EPLL 0x10
+#define VPLL 0x20
+
+/* groups of registers */
+#define PLL_LOCK 0x000
+#define PLL_CON 0x100
+#define CLK_SRC 0x200
+#define CLK_SRC_MASK 0x300
+#define CLK_MUX_STAT 0x400
+#define CLK_DIV 0x500
+#define CLK_DIV_STAT 0x600
+#define CLK_GATE_SCLK 0x800
+#define CLK_GATE_IP 0x900
+
+#define GROUP_MASK 0xF00
+
+#define PLL_LOCK_(pll) (PLL_LOCK + pll)
+#define PLL_CON0_(pll) (PLL_CON + pll)
+#define PLL_CON1_(pll) (PLL_CON + pll + 4)
+
+#define CLK_SRC_(block) (CLK_SRC + block)
+#define CLK_SRC_MASK_(block) (CLK_SRC_MASK + block)
+#define CLK_MUX_STAT_(block) (CLK_MUX_STAT + block)
+#define CLK_DIV_(block) (CLK_DIV + block)
+#define CLKDIV2_RATIO 0x580 /* described for CMU_TOP only */
+#define CLK_DIV_STAT_(block) (CLK_DIV_STAT + block)
+#define CLKDIV2_STAT 0x680 /* described for CMU_TOP only */
+#define CLK_GATE_SCLK_(block) (CLK_GATE_SCLK + block)
+/* For CMU_LEFTBUS and CMU_RIGHTBUS, CLK_GATE_IP_XXX
+ registers are located at 0x800. */
+#define CLK_GATE_IP_LR_BUS 0x800
+#define CLK_GATE_IP_(block) (CLK_GATE_IP + block)
+#define CLK_GATE_BLOCK 0x970 /* described for CMU_TOP only */
+#define CLKOUT_CMU 0xA00
+#define CLKOUT_CMU_DIV_STAT 0xA04
+
+/*
+ * registers which are located outside of 0xAFF region
+ */
+/* CMU_DMC */
+#define DCGIDX_MAP0 0x01000
+#define DCGIDX_MAP1 0x01004
+#define DCGIDX_MAP2 0x01008
+#define DCGPERF_MAP0 0x01020
+#define DCGPERF_MAP1 0x01024
+#define DVCIDX_MAP 0x01040
+#define FREQ_CPU 0x01060
+#define FREQ_DPM 0x01064
+#define DVSEMCLK_EN 0x01080
+#define MAXPERF 0x01084
+/* CMU_CPU */
+#define ARMCLK_STOPCTRL 0x01000
+#define ATCLK_STOPCTRL 0x01004
+#define PARITYFAIL_STATUS 0x01010
+#define PARITYFAIL_CLEAR 0x01014
+#define PWR_CTRL 0x01020
+#define APLL_CON0_L8 0x01100
+#define APLL_CON0_L7 0x01104
+#define APLL_CON0_L6 0x01108
+#define APLL_CON0_L5 0x0110C
+#define APLL_CON0_L4 0x01110
+#define APLL_CON0_L3 0x01114
+#define APLL_CON0_L2 0x01118
+#define APLL_CON0_L1 0x0111C
+#define IEM_CONTROL 0x01120
+#define APLL_CON1_L8 0x01200
+#define APLL_CON1_L7 0x01204
+#define APLL_CON1_L6 0x01208
+#define APLL_CON1_L5 0x0120C
+#define APLL_CON1_L4 0x01210
+#define APLL_CON1_L3 0x01214
+#define APLL_CON1_L2 0x01218
+#define APLL_CON1_L1 0x0121C
+#define CLKDIV_IEM_L8 0x01300
+#define CLKDIV_IEM_L7 0x01304
+#define CLKDIV_IEM_L6 0x01308
+#define CLKDIV_IEM_L5 0x0130C
+#define CLKDIV_IEM_L4 0x01310
+#define CLKDIV_IEM_L3 0x01314
+#define CLKDIV_IEM_L2 0x01318
+#define CLKDIV_IEM_L1 0x0131C
+
+#define EXTENDED_REGION_MASK 0x1000
+#define EXTENDED_REGISTER_MASK 0xFFF
+
+typedef struct {
+ const char *name; /* for debugging */
+ uint32_t offset;
+ uint32_t reset_value;
+} Exynos4210CmuReg;
+
+
+static Exynos4210CmuReg exynos4210_cmu_leftbus_regs[] = {
+ /* CMU_LEFTBUS registers */
+ {"CLK_SRC_LEFTBUS", CLK_SRC_(LEFTBUS_BLK), 0x00000000},
+ {"CLK_MUX_STAT_LEFTBUS", CLK_MUX_STAT_(LEFTBUS_BLK), 0x00000001},
+ {"CLK_DIV_LEFTBUS", CLK_DIV_(LEFTBUS_BLK), 0x00000000},
+ {"CLK_DIV_STAT_LEFTBUS", CLK_DIV_STAT_(LEFTBUS_BLK), 0x00000000},
+ {"CLK_GATE_IP_LEFTBUS", CLK_GATE_IP_LR_BUS, 0xFFFFFFFF},
+ {"CLKOUT_CMU_LEFTBUS", CLKOUT_CMU, 0x00010000},
+ {"CLKOUT_CMU_LEFTBUS_DIV_STAT", CLKOUT_CMU_DIV_STAT, 0x00000000},
+};
+
+static Exynos4210CmuReg exynos4210_cmu_rightbus_regs[] = {
+ /* CMU_RIGHTBUS registers */
+ {"CLK_SRC_RIGHTBUS", CLK_SRC_(RIGHTBUS_BLK), 0x00000000},
+ {"CLK_MUX_STAT_RIGHTBUS", CLK_MUX_STAT_(RIGHTBUS_BLK), 0x00000001},
+ {"CLK_DIV_RIGHTBUS", CLK_DIV_(RIGHTBUS_BLK), 0x00000000},
+ {"CLK_DIV_STAT_RIGHTBUS", CLK_DIV_STAT_(RIGHTBUS_BLK), 0x00000000},
+ {"CLK_GATE_IP_RIGHTBUS", CLK_GATE_IP_LR_BUS, 0xFFFFFFFF},
+ {"CLKOUT_CMU_RIGHTBUS", CLKOUT_CMU, 0x00010000},
+ {"CLKOUT_CMU_RIGHTBUS_DIV_STAT", CLKOUT_CMU_DIV_STAT, 0x00000000},
+};
+
+static Exynos4210CmuReg exynos4210_cmu_top_regs[] = {
+ /* CMU_TOP registers */
+ {"EPLL_LOCK", PLL_LOCK_(EPLL), 0x00000FFF},
+ {"VPLL_LOCK", PLL_LOCK_(VPLL), 0x00000FFF},
+ {"EPLL_CON0", PLL_CON0_(EPLL), 0x00300301},
+ {"EPLL_CON1", PLL_CON1_(EPLL), 0x00000000},
+ {"VPLL_CON0", PLL_CON0_(VPLL), 0x00240201},
+ {"VPLL_CON1", PLL_CON1_(VPLL), 0x66010464},
+ {"CLK_SRC_TOP0", CLK_SRC_(TOP0_BLK), 0x00000000},
+ {"CLK_SRC_TOP1", CLK_SRC_(TOP1_BLK), 0x00000000},
+ {"CLK_SRC_CAM", CLK_SRC_(CAM_BLK), 0x11111111},
+ {"CLK_SRC_TV", CLK_SRC_(TV_BLK), 0x00000000},
+ {"CLK_SRC_MFC", CLK_SRC_(MFC_BLK), 0x00000000},
+ {"CLK_SRC_G3D", CLK_SRC_(G3D_BLK), 0x00000000},
+ {"CLK_SRC_IMAGE", CLK_SRC_(IMAGE_BLK), 0x00000000},
+ {"CLK_SRC_LCD0", CLK_SRC_(LCD0_BLK), 0x00001111},
+ {"CLK_SRC_LCD1", CLK_SRC_(LCD1_BLK), 0x00001111},
+ {"CLK_SRC_MAUDIO", CLK_SRC_(MAUDIO_BLK), 0x00000005},
+ {"CLK_SRC_FSYS", CLK_SRC_(FSYS_BLK), 0x00011111},
+ {"CLK_SRC_PERIL0", CLK_SRC_(PERIL0_BLK), 0x00011111},
+ {"CLK_SRC_PERIL1", CLK_SRC_(PERIL1_BLK), 0x01110055},
+ {"CLK_SRC_MASK_TOP", CLK_SRC_MASK_(TOP_BLK), 0x00000001},
+ {"CLK_SRC_MASK_CAM", CLK_SRC_MASK_(CAM_BLK), 0x11111111},
+ {"CLK_SRC_MASK_TV", CLK_SRC_MASK_(TV_BLK), 0x00000111},
+ {"CLK_SRC_MASK_LCD0", CLK_SRC_MASK_(LCD0_BLK), 0x00001111},
+ {"CLK_SRC_MASK_LCD1", CLK_SRC_MASK_(LCD1_BLK), 0x00001111},
+ {"CLK_SRC_MASK_MAUDIO", CLK_SRC_MASK_(MAUDIO_BLK), 0x00000001},
+ {"CLK_SRC_MASK_FSYS", CLK_SRC_MASK_(FSYS_BLK), 0x01011111},
+ {"CLK_SRC_MASK_PERIL0", CLK_SRC_MASK_(PERIL0_BLK), 0x00011111},
+ {"CLK_SRC_MASK_PERIL1", CLK_SRC_MASK_(PERIL1_BLK), 0x01110111},
+ {"CLK_MUX_STAT_TOP", CLK_MUX_STAT_(TOP_BLK), 0x11111111},
+ {"CLK_MUX_STAT_MFC", CLK_MUX_STAT_(MFC_BLK), 0x00000111},
+ {"CLK_MUX_STAT_G3D", CLK_MUX_STAT_(G3D_BLK), 0x00000111},
+ {"CLK_MUX_STAT_IMAGE", CLK_MUX_STAT_(IMAGE_BLK), 0x00000111},
+ {"CLK_DIV_TOP", CLK_DIV_(TOP_BLK), 0x00000000},
+ {"CLK_DIV_CAM", CLK_DIV_(CAM_BLK), 0x00000000},
+ {"CLK_DIV_TV", CLK_DIV_(TV_BLK), 0x00000000},
+ {"CLK_DIV_MFC", CLK_DIV_(MFC_BLK), 0x00000000},
+ {"CLK_DIV_G3D", CLK_DIV_(G3D_BLK), 0x00000000},
+ {"CLK_DIV_IMAGE", CLK_DIV_(IMAGE_BLK), 0x00000000},
+ {"CLK_DIV_LCD0", CLK_DIV_(LCD0_BLK), 0x00700000},
+ {"CLK_DIV_LCD1", CLK_DIV_(LCD1_BLK), 0x00700000},
+ {"CLK_DIV_MAUDIO", CLK_DIV_(MAUDIO_BLK), 0x00000000},
+ {"CLK_DIV_FSYS0", CLK_DIV_(FSYS0_BLK), 0x00B00000},
+ {"CLK_DIV_FSYS1", CLK_DIV_(FSYS1_BLK), 0x00000000},
+ {"CLK_DIV_FSYS2", CLK_DIV_(FSYS2_BLK), 0x00000000},
+ {"CLK_DIV_FSYS3", CLK_DIV_(FSYS3_BLK), 0x00000000},
+ {"CLK_DIV_PERIL0", CLK_DIV_(PERIL0_BLK), 0x00000000},
+ {"CLK_DIV_PERIL1", CLK_DIV_(PERIL1_BLK), 0x00000000},
+ {"CLK_DIV_PERIL2", CLK_DIV_(PERIL2_BLK), 0x00000000},
+ {"CLK_DIV_PERIL3", CLK_DIV_(PERIL3_BLK), 0x00000000},
+ {"CLK_DIV_PERIL4", CLK_DIV_(PERIL4_BLK), 0x00000000},
+ {"CLK_DIV_PERIL5", CLK_DIV_(PERIL5_BLK), 0x00000000},
+ {"CLKDIV2_RATIO", CLKDIV2_RATIO, 0x11111111},
+ {"CLK_DIV_STAT_TOP", CLK_DIV_STAT_(TOP_BLK), 0x00000000},
+ {"CLK_DIV_STAT_CAM", CLK_DIV_STAT_(CAM_BLK), 0x00000000},
+ {"CLK_DIV_STAT_TV", CLK_DIV_STAT_(TV_BLK), 0x00000000},
+ {"CLK_DIV_STAT_MFC", CLK_DIV_STAT_(MFC_BLK), 0x00000000},
+ {"CLK_DIV_STAT_G3D", CLK_DIV_STAT_(G3D_BLK), 0x00000000},
+ {"CLK_DIV_STAT_IMAGE", CLK_DIV_STAT_(IMAGE_BLK), 0x00000000},
+ {"CLK_DIV_STAT_LCD0", CLK_DIV_STAT_(LCD0_BLK), 0x00000000},
+ {"CLK_DIV_STAT_LCD1", CLK_DIV_STAT_(LCD1_BLK), 0x00000000},
+ {"CLK_DIV_STAT_MAUDIO", CLK_DIV_STAT_(MAUDIO_BLK), 0x00000000},
+ {"CLK_DIV_STAT_FSYS0", CLK_DIV_STAT_(FSYS0_BLK), 0x00000000},
+ {"CLK_DIV_STAT_FSYS1", CLK_DIV_STAT_(FSYS1_BLK), 0x00000000},
+ {"CLK_DIV_STAT_FSYS2", CLK_DIV_STAT_(FSYS2_BLK), 0x00000000},
+ {"CLK_DIV_STAT_FSYS3", CLK_DIV_STAT_(FSYS3_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL0", CLK_DIV_STAT_(PERIL0_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL1", CLK_DIV_STAT_(PERIL1_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL2", CLK_DIV_STAT_(PERIL2_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL3", CLK_DIV_STAT_(PERIL3_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL4", CLK_DIV_STAT_(PERIL4_BLK), 0x00000000},
+ {"CLK_DIV_STAT_PERIL5", CLK_DIV_STAT_(PERIL5_BLK), 0x00000000},
+ {"CLKDIV2_STAT", CLKDIV2_STAT, 0x00000000},
+ {"CLK_GATE_SCLK_CAM", CLK_GATE_SCLK_(CAM_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_CAM", CLK_GATE_IP_(CAM_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_TV", CLK_GATE_IP_(TV_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_MFC", CLK_GATE_IP_(MFC_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_G3D", CLK_GATE_IP_(G3D_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_IMAGE", CLK_GATE_IP_(IMAGE_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_LCD0", CLK_GATE_IP_(LCD0_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_LCD1", CLK_GATE_IP_(LCD1_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_FSYS", CLK_GATE_IP_(FSYS_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_GPS", CLK_GATE_IP_(GPS_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_PERIL", CLK_GATE_IP_(PERIL_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_PERIR", CLK_GATE_IP_(PERIR_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_BLOCK", CLK_GATE_BLOCK, 0xFFFFFFFF},
+ {"CLKOUT_CMU_TOP", CLKOUT_CMU, 0x00010000},
+ {"CLKOUT_CMU_TOP_DIV_STAT", CLKOUT_CMU_DIV_STAT, 0x00000000},
+};
+
+static Exynos4210CmuReg exynos4210_cmu_dmc_regs[] = {
+ /* CMU_DMC registers */
+ {"CLK_SRC_DMC", CLK_SRC_(DMC_BLK), 0x00010000},
+ {"CLK_SRC_MASK_DMC", CLK_SRC_MASK_(DMC_BLK), 0x00010000},
+ {"CLK_MUX_STAT_DMC", CLK_MUX_STAT_(DMC_BLK), 0x11100110},
+ {"CLK_DIV_DMC0", CLK_DIV_(DMC0_BLK), 0x00000000},
+ {"CLK_DIV_DMC1", CLK_DIV_(DMC1_BLK), 0x00000000},
+ {"CLK_DIV_STAT_DMC0", CLK_DIV_STAT_(DMC0_BLK), 0x00000000},
+ {"CLK_DIV_STAT_DMC1", CLK_DIV_STAT_(DMC1_BLK), 0x00000000},
+ {"CLK_GATE_IP_DMC", CLK_GATE_IP_(DMC_BLK), 0xFFFFFFFF},
+ {"CLKOUT_CMU_DMC", CLKOUT_CMU, 0x00010000},
+ {"CLKOUT_CMU_DMC_DIV_STAT", CLKOUT_CMU_DIV_STAT, 0x00000000},
+ {"DCGIDX_MAP0", DCGIDX_MAP0, 0xFFFFFFFF},
+ {"DCGIDX_MAP1", DCGIDX_MAP1, 0xFFFFFFFF},
+ {"DCGIDX_MAP2", DCGIDX_MAP2, 0xFFFFFFFF},
+ {"DCGPERF_MAP0", DCGPERF_MAP0, 0xFFFFFFFF},
+ {"DCGPERF_MAP1", DCGPERF_MAP1, 0xFFFFFFFF},
+ {"DVCIDX_MAP", DVCIDX_MAP, 0xFFFFFFFF},
+ {"FREQ_CPU", FREQ_CPU, 0x00000000},
+ {"FREQ_DPM", FREQ_DPM, 0x00000000},
+ {"DVSEMCLK_EN", DVSEMCLK_EN, 0x00000000},
+ {"MAXPERF", MAXPERF, 0x00000000},
+};
+
+static Exynos4210CmuReg exynos4210_cmu_cpu_regs[] = {
+ /* CMU_CPU registers */
+ {"APLL_LOCK", PLL_LOCK_(APLL), 0x00000FFF},
+ {"MPLL_LOCK", PLL_LOCK_(MPLL), 0x00000FFF},
+ {"APLL_CON0", PLL_CON0_(APLL), 0x00C80601},
+ {"APLL_CON1", PLL_CON1_(APLL), 0x0000001C},
+ {"MPLL_CON0", PLL_CON0_(MPLL), 0x00C80601},
+ {"MPLL_CON1", PLL_CON1_(MPLL), 0x0000001C},
+ {"CLK_SRC_CPU", CLK_SRC_(CPU_BLK), 0x00000000},
+ {"CLK_MUX_STAT_CPU", CLK_MUX_STAT_(CPU_BLK), 0x00110101},
+ {"CLK_DIV_CPU0", CLK_DIV_(CPU0_BLK), 0x00000000},
+ {"CLK_DIV_CPU1", CLK_DIV_(CPU1_BLK), 0x00000000},
+ {"CLK_DIV_STAT_CPU0", CLK_DIV_STAT_(CPU0_BLK), 0x00000000},
+ {"CLK_DIV_STAT_CPU1", CLK_DIV_STAT_(CPU1_BLK), 0x00000000},
+ {"CLK_GATE_SCLK_CPU", CLK_GATE_SCLK_(CPU_BLK), 0xFFFFFFFF},
+ {"CLK_GATE_IP_CPU", CLK_GATE_IP_(CPU_BLK), 0xFFFFFFFF},
+ {"CLKOUT_CMU_CPU", CLKOUT_CMU, 0x00010000},
+ {"CLKOUT_CMU_CPU_DIV_STAT", CLKOUT_CMU_DIV_STAT, 0x00000000},
+ {"ARMCLK_STOPCTRL", ARMCLK_STOPCTRL, 0x00000044},
+ {"ATCLK_STOPCTRL", ATCLK_STOPCTRL, 0x00000044},
+ {"PARITYFAIL_STATUS", PARITYFAIL_STATUS, 0x00000000},
+ {"PARITYFAIL_CLEAR", PARITYFAIL_CLEAR, 0x00000000},
+ {"PWR_CTRL", PWR_CTRL, 0x00000033},
+ {"APLL_CON0_L8", APLL_CON0_L8, 0x00C80601},
+ {"APLL_CON0_L7", APLL_CON0_L7, 0x00C80601},
+ {"APLL_CON0_L6", APLL_CON0_L6, 0x00C80601},
+ {"APLL_CON0_L5", APLL_CON0_L5, 0x00C80601},
+ {"APLL_CON0_L4", APLL_CON0_L4, 0x00C80601},
+ {"APLL_CON0_L3", APLL_CON0_L3, 0x00C80601},
+ {"APLL_CON0_L2", APLL_CON0_L2, 0x00C80601},
+ {"APLL_CON0_L1", APLL_CON0_L1, 0x00C80601},
+ {"IEM_CONTROL", IEM_CONTROL, 0x00000000},
+ {"APLL_CON1_L8", APLL_CON1_L8, 0x00000000},
+ {"APLL_CON1_L7", APLL_CON1_L7, 0x00000000},
+ {"APLL_CON1_L6", APLL_CON1_L6, 0x00000000},
+ {"APLL_CON1_L5", APLL_CON1_L5, 0x00000000},
+ {"APLL_CON1_L4", APLL_CON1_L4, 0x00000000},
+ {"APLL_CON1_L3", APLL_CON1_L3, 0x00000000},
+ {"APLL_CON1_L2", APLL_CON1_L2, 0x00000000},
+ {"APLL_CON1_L1", APLL_CON1_L1, 0x00000000},
+ {"CLKDIV_IEM_L8", CLKDIV_IEM_L8, 0x00000000},
+ {"CLKDIV_IEM_L7", CLKDIV_IEM_L7, 0x00000000},
+ {"CLKDIV_IEM_L6", CLKDIV_IEM_L6, 0x00000000},
+ {"CLKDIV_IEM_L5", CLKDIV_IEM_L5, 0x00000000},
+ {"CLKDIV_IEM_L4", CLKDIV_IEM_L4, 0x00000000},
+ {"CLKDIV_IEM_L3", CLKDIV_IEM_L3, 0x00000000},
+ {"CLKDIV_IEM_L2", CLKDIV_IEM_L2, 0x00000000},
+ {"CLKDIV_IEM_L1", CLKDIV_IEM_L1, 0x00000000},
+};
+
+#define EXYNOS4210_CMU_REGS_MEM_SIZE 0x4000
+
+/*
+ * for indexing register in the uint32_t array
+ *
+ * 'reg' - register offset (see offsets definitions above)
+ *
+ */
+#define I_(reg) (reg / sizeof(uint32_t))
+
+#define XOM_0 1 /* Select XXTI (0) or XUSBXTI (1) base clock source */
+
+/*
+ * Offsets in CLK_SRC_CPU register
+ * for control MUXMPLL and MUXAPLL
+ *
+ * 0 = FINPLL, 1 = MOUTM(A)PLLFOUT
+ */
+#define MUX_APLL_SEL_SHIFT 0
+#define MUX_MPLL_SEL_SHIFT 8
+#define MUX_CORE_SEL_SHIFT 16
+#define MUX_HPM_SEL_SHIFT 20
+
+#define MUX_APLL_SEL (1 << MUX_APLL_SEL_SHIFT)
+#define MUX_MPLL_SEL (1 << MUX_MPLL_SEL_SHIFT)
+#define MUX_CORE_SEL (1 << MUX_CORE_SEL_SHIFT)
+#define MUX_HPM_SEL (1 << MUX_HPM_SEL_SHIFT)
+
+/* Offsets for fields in CLK_MUX_STAT_CPU register */
+#define APLL_SEL_SHIFT 0
+#define APLL_SEL_MASK 0x00000007
+#define MPLL_SEL_SHIFT 8
+#define MPLL_SEL_MASK 0x00000700
+#define CORE_SEL_SHIFT 16
+#define CORE_SEL_MASK 0x00070000
+#define HPM_SEL_SHIFT 20
+#define HPM_SEL_MASK 0x00700000
+
+
+/* Offsets for fields in <pll>_CON0 register */
+#define PLL_ENABLE_SHIFT 31
+#define PLL_ENABLE_MASK 0x80000000 /* [31] bit */
+#define PLL_LOCKED_MASK 0x20000000 /* [29] bit */
+#define PLL_MDIV_SHIFT 16
+#define PLL_MDIV_MASK 0x03FF0000 /* [25:16] bits */
+#define PLL_PDIV_SHIFT 8
+#define PLL_PDIV_MASK 0x00003F00 /* [13:8] bits */
+#define PLL_SDIV_SHIFT 0
+#define PLL_SDIV_MASK 0x00000007 /* [2:0] bits */
+
+/*
+ * Offset in CLK_DIV_CPU0 register
+ * for DIVAPLL clock divider ratio
+ */
+#define APLL_RATIO_SHIFT 24
+#define APLL_RATIO_MASK 0x07000000 /* [26:24] bits */
+
+/*
+ * Offset in CLK_DIV_TOP register
+ * for DIVACLK_100 clock divider ratio
+ */
+#define ACLK_100_RATIO_SHIFT 4
+#define ACLK_100_RATIO_MASK 0x000000f0 /* [7:4] bits */
+
+/* Offset in CLK_SRC_TOP0 register */
+#define MUX_ACLK_100_SEL_SHIFT 16
+
+/*
+ * Offsets in CLK_SRC_PERIL0 register
+ * for clock sources of UARTs
+ */
+#define UART0_SEL_SHIFT 0
+#define UART1_SEL_SHIFT 4
+#define UART2_SEL_SHIFT 8
+#define UART3_SEL_SHIFT 12
+#define UART4_SEL_SHIFT 16
+/*
+ * Offsets in CLK_DIV_PERIL0 register
+ * for clock divider of UARTs
+ */
+#define UART0_DIV_SHIFT 0
+#define UART1_DIV_SHIFT 4
+#define UART2_DIV_SHIFT 8
+#define UART3_DIV_SHIFT 12
+#define UART4_DIV_SHIFT 16
+
+#define SOURCES_NUMBER 9
+
+typedef struct ClockChangeEntry {
+ QTAILQ_ENTRY(ClockChangeEntry) entry;
+ ClockChangeHandler *func;
+ void *opaque;
+} ClockChangeEntry;
+
+#define TYPE_EXYNOS4210_CMU "exynos4210.cmu"
+
+typedef struct {
+
+ const char *name;
+ Exynos4210Clock id;
+ uint64_t rate;
+
+ /* Current source clock */
+ Exynos4210Clock src_id;
+ /*
+ * Available sources. Their order must correspond to CLK_SRC_ register
+ */
+ Exynos4210Clock src_ids[SOURCES_NUMBER];
+
+ uint32_t src_reg; /* Offset of CLK_SRC_<*> register */
+ uint32_t div_reg; /* Offset of CLK_DIV_<*> register */
+
+ /*
+ * Shift for MUX_<clk>_SEL value which is stored
+ * in appropriate CLK_MUX_STAT_<cmu> register
+ */
+ uint8_t mux_shift;
+
+ /*
+ * Shift for <clk>_RATIO value which is stored
+ * in appropriate CLK_DIV_<cmu> register
+ */
+ uint8_t div_shift;
+
+ /* Which CMU controls this clock */
+ Exynos4210Cmu cmu_id;
+
+ QTAILQ_HEAD(, ClockChangeEntry) clock_change_handler;
+
+} Exynos4210ClockState;
+
+
+typedef struct {
+
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* registers values */
+ uint32_t reg[EXYNOS4210_CMU_REGS_MEM_SIZE];
+
+ /* which CMU it is */
+ Exynos4210Cmu cmu_id;
+
+ /* registers information for debugging and resetting */
+ Exynos4210CmuReg *regs;
+ int regs_number;
+
+ Exynos4210ClockState *clock;
+ int clock_number; /* how many clocks are controlled by given CMU */
+
+} Exynos4210CmuState;
+
+
+/* Clocks from Clock Pads */
+/*
+ * Two following clocks aren't controlled by any CMUs. These structures are
+ * used directly from global space and their fields shouldn't be modified.
+ */
+
+/* It should be used only for testing purposes. XOM_0 is 0 */
+static Exynos4210ClockState xxti = {
+ .name = "XXTI",
+ .id = EXYNOS4210_XXTI,
+ .rate = 24000000,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+/* Main source. XOM_0 is 1 */
+static Exynos4210ClockState xusbxti = {
+ .name = "XUSBXTI",
+ .id = EXYNOS4210_XUSBXTI,
+ .rate = 24000000,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+//static Exynos4210ClockState usb_phy = {
+// .name = "USB_PHY",
+// .id = EXYNOS4210_USB_PHY,
+// .src_id = EXYNOS4210_XUSBXTI,
+// .cmu_id = UNSPECIFIED_CMU,
+//};
+//
+//static Exynos4210ClockState usb_host_phy = {
+// .name = "USB_HOST_PHY",
+// .id = EXYNOS4210_USB_HOST_PHY,
+// .src_id = EXYNOS4210_XUSBXTI,
+// .cmu_id = UNSPECIFIED_CMU,
+//};
+//
+//static Exynos4210ClockState hdmi_phy = {
+// .name = "HDMI_PHY",
+// .id = EXYNOS4210_HDMI_PHY,
+// .src_id = EXYNOS4210_XUSBXTI,
+// .cmu_id = UNSPECIFIED_CMU,
+//};
+
+
+/* PLLs */
+
+static Exynos4210ClockState mpll = {
+ .name = "MPLL",
+ .id = EXYNOS4210_MPLL,
+ .src_id = (XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI),
+ .div_reg = PLL_CON0_(MPLL),
+ .cmu_id = EXYNOS4210_CMU_CPU,
+};
+
+static Exynos4210ClockState apll = {
+ .name = "APLL",
+ .id = EXYNOS4210_APLL,
+ .src_id = (XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI),
+ .div_reg = PLL_CON0_(APLL),
+ .cmu_id = EXYNOS4210_CMU_CPU,
+};
+
+
+/**/
+
+static Exynos4210ClockState sclk_hdmi24m = {
+ .name = "SCLK_HDMI24M",
+ .id = EXYNOS4210_SCLK_HDMI24M,
+ .rate = 24000000,
+// .src_id = EXYNOS4210_HDMI_PHY,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_usbphy0 = {
+ .name = "SCLK_USBPHY0",
+ .id = EXYNOS4210_SCLK_USBPHY0,
+ .rate = 24000000,
+// .src_id = EXYNOS4210_USB_PHY,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_usbphy1 = {
+ .name = "SCLK_USBPHY1",
+ .id = EXYNOS4210_SCLK_USBPHY1,
+ .rate = 24000000,
+// .src_id = EXYNOS4210_USB_HOST_PHY,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_hdmiphy = {
+ .name = "SCLK_HDMIPHY",
+ .id = EXYNOS4210_SCLK_HDMIPHY,
+ .rate = 24000000,
+// .src_id = EXYNOS4210_HDMI_PHY,
+ .cmu_id = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_mpll = {
+ .name = "SCLK_MPLL",
+ .id = EXYNOS4210_SCLK_MPLL,
+ .src_ids = {XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI,
+ EXYNOS4210_MPLL},
+ .src_reg = CLK_SRC_(CPU_BLK),
+ .mux_shift = MUX_MPLL_SEL_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_CPU,
+};
+
+static Exynos4210ClockState sclk_apll = {
+ .name = "SCLK_APLL",
+ .id = EXYNOS4210_SCLK_APLL,
+ .src_ids = {XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI,
+ EXYNOS4210_APLL},
+ .src_reg = CLK_SRC_(CPU_BLK),
+ .div_reg = CLK_DIV_(CPU0_BLK),
+ .mux_shift = MUX_APLL_SEL_SHIFT,
+ .div_shift = APLL_RATIO_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_CPU,
+};
+
+static Exynos4210ClockState aclk_100 = {
+ .name = "ACLK_100",
+ .id = EXYNOS4210_ACLK_100,
+ .src_ids = {EXYNOS4210_SCLK_MPLL, EXYNOS4210_SCLK_APLL},
+ .src_reg = CLK_SRC_(TOP0_BLK),
+ .div_reg = CLK_DIV_(TOP_BLK),
+ .mux_shift = MUX_ACLK_100_SEL_SHIFT,
+ .div_shift = ACLK_100_RATIO_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+static Exynos4210ClockState sclk_uart0 = {
+ .name = "SCLK_UART0",
+ .id = EXYNOS4210_SCLK_UART0,
+ .src_ids = {EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_MPLL},
+ .src_reg = CLK_SRC_(PERIL0_BLK),
+ .div_reg = CLK_DIV_(PERIL0_BLK),
+ .mux_shift = UART0_SEL_SHIFT,
+ .div_shift = UART0_DIV_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+static Exynos4210ClockState sclk_uart1 = {
+ .name = "SCLK_UART1",
+ .id = EXYNOS4210_SCLK_UART1,
+ .src_ids = {EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_MPLL},
+ .src_reg = CLK_SRC_(PERIL0_BLK),
+ .div_reg = CLK_DIV_(PERIL0_BLK),
+ .mux_shift = UART1_SEL_SHIFT,
+ .div_shift = UART1_DIV_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+static Exynos4210ClockState sclk_uart2 = {
+ .name = "SCLK_UART2",
+ .id = EXYNOS4210_SCLK_UART2,
+ .src_ids = {EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_MPLL},
+ .src_reg = CLK_SRC_(PERIL0_BLK),
+ .div_reg = CLK_DIV_(PERIL0_BLK),
+ .mux_shift = UART2_SEL_SHIFT,
+ .div_shift = UART2_DIV_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+static Exynos4210ClockState sclk_uart3 = {
+ .name = "SCLK_UART3",
+ .id = EXYNOS4210_SCLK_UART3,
+ .src_ids = {EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_MPLL},
+ .src_reg = CLK_SRC_(PERIL0_BLK),
+ .div_reg = CLK_DIV_(PERIL0_BLK),
+ .mux_shift = UART3_SEL_SHIFT,
+ .div_shift = UART3_DIV_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+static Exynos4210ClockState sclk_uart4 = {
+ .name = "SCLK_UART4",
+ .id = EXYNOS4210_SCLK_UART4,
+ .src_ids = {EXYNOS4210_XXTI,
+ EXYNOS4210_XUSBXTI,
+ EXYNOS4210_SCLK_HDMI24M,
+ EXYNOS4210_SCLK_USBPHY0,
+ EXYNOS4210_SCLK_USBPHY1,
+ EXYNOS4210_SCLK_HDMIPHY,
+ EXYNOS4210_SCLK_MPLL},
+ .src_reg = CLK_SRC_(PERIL0_BLK),
+ .div_reg = CLK_DIV_(PERIL0_BLK),
+ .mux_shift = UART4_SEL_SHIFT,
+ .div_shift = UART4_DIV_SHIFT,
+ .cmu_id = EXYNOS4210_CMU_TOP,
+};
+
+/*
+ * This array must correspond to Exynos4210Clock enumerator
+ * which is defined in exynos4210.h file
+ *
+ */
+static Exynos4210ClockState *exynos4210_clock[] = {
+ NULL,
+ &xxti,
+ &xusbxti,
+// &usb_phy,
+// &usb_host_phy,
+// &hdmi_phy,
+ &apll,
+ &mpll,
+ &sclk_hdmi24m,
+ &sclk_usbphy0,
+ &sclk_usbphy1,
+ &sclk_hdmiphy,
+ &sclk_apll,
+ &sclk_mpll,
+ &aclk_100,
+ &sclk_uart0,
+ &sclk_uart1,
+ &sclk_uart2,
+ &sclk_uart3,
+ &sclk_uart4,
+ NULL,
+};
+
+/*
+ * This array must correspond to Exynos4210Cmu enumerator
+ * which is defined in exynos4210.h file
+ *
+ */
+static char exynos4210_cmu_path[][13] = {
+ "cmu_leftbus",
+ "cmu_rightbus",
+ "cmu_top",
+ "cmu_dmc",
+ "cmu_cpu",
+};
+
+#if DEBUG_CMU_EXTEND
+/* The only meaning of life - debugging. This function should be only used
+ * inside PRINT_DEBUG_EXTEND macros
+ */
+static const char *exynos4210_cmu_regname(Exynos4210CmuState *s,
+ target_phys_addr_t offset)
+{
+ int i;
+
+ for (i = 0; i < s->regs_number; i++) {
+ if (offset == s->regs[i].offset) {
+ return s->regs[i].name;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+
+static Exynos4210ClockState *exynos4210_clock_find(Exynos4210Clock clock_id)
+{
+ int i;
+ int cmu_id;
+ Object *cmu;
+ Exynos4210CmuState *s;
+
+ cmu_id = exynos4210_clock[clock_id]->cmu_id;
+
+ if (cmu_id == UNSPECIFIED_CMU) {
+ for (i = 1; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+ if (exynos4210_clock[i]->id == clock_id) {
+
+ PRINT_DEBUG("Clock %s [%p] in CMU %d have been found\n",
+ exynos4210_clock[i]->name,
+ exynos4210_clock[i],
+ cmu_id);
+
+ return exynos4210_clock[i];
+ }
+ }
+ }
+
+ cmu = object_resolve_path(exynos4210_cmu_path[cmu_id], NULL);
+ s = OBJECT_CHECK(Exynos4210CmuState, cmu, TYPE_EXYNOS4210_CMU);
+
+ for (i = 0; i < s->clock_number; i++) {
+ if (s->clock[i].id == clock_id) {
+
+ PRINT_DEBUG("Clock %s [%p] in CMU %d have been found\n",
+ s->clock[i].name,
+ &s->clock[i],
+ s->clock[i].cmu_id);
+ return &s->clock[i];
+ }
+ }
+
+ PRINT_ERROR("Clock %d not found\n", clock_id);
+
+ return NULL;
+}
+
+
+void exynos4210_register_clock_handler(ClockChangeHandler *func,
+ Exynos4210Clock clock_id, void *opaque)
+{
+ ClockChangeEntry *cce = g_malloc0(sizeof(ClockChangeEntry));
+ Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+
+ if (clock == NULL) {
+ hw_error("We aren't be able to find clock %d\n", clock_id);
+ } else if (clock->cmu_id == UNSPECIFIED_CMU) {
+
+ PRINT_DEBUG("Clock %s never are changed. Handler won't be set.",
+ exynos4210_clock[clock_id]->name);
+
+ return;
+ }
+
+ cce->func = func;
+ cce->opaque = opaque;
+
+ QTAILQ_INSERT_TAIL(&clock->clock_change_handler, cce, entry);
+
+ PRINT_DEBUG("For %s have been set handler [%p]\n", clock->name, cce->func);
+
+ return;
+}
+
+uint64_t exynos4210_cmu_get_rate(Exynos4210Clock clock_id)
+{
+ Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+
+ if (clock == NULL) {
+ hw_error("We aren't be able to find clock %d\n", clock_id);
+ }
+
+ return clock->rate;
+}
+
+static void exynos4210_cmu_set_pll(void *opaque, Exynos4210ClockState *pll)
+{
+ Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+ Exynos4210ClockState *source;
+ target_phys_addr_t offset = pll->div_reg;
+ ClockChangeEntry *cce;
+ uint32_t pdiv, mdiv, sdiv, enable;
+
+ source = exynos4210_clock_find(pll->src_id);
+
+ if (source == NULL) {
+ hw_error("We haven't find source clock %d (requested for %s)\n",
+ pll->src_id, pll->name);
+ }
+
+ /*
+ * FOUT = MDIV * FIN / (PDIV * 2^(SDIV-1))
+ */
+
+ enable = (s->reg[I_(offset)] & PLL_ENABLE_MASK) >> PLL_ENABLE_SHIFT;
+ mdiv = (s->reg[I_(offset)] & PLL_MDIV_MASK) >> PLL_MDIV_SHIFT;
+ pdiv = (s->reg[I_(offset)] & PLL_PDIV_MASK) >> PLL_PDIV_SHIFT;
+ sdiv = (s->reg[I_(offset)] & PLL_SDIV_MASK) >> PLL_SDIV_SHIFT;
+
+ if (source) {
+ if (enable) {
+ pll->rate = mdiv * source->rate / (pdiv * (1 << (sdiv-1)));
+ } else {
+ pll->rate = 0;
+ }
+ } else {
+ hw_error("%s: Source undefined for %s\n", __FUNCTION__, pll->name);
+ }
+
+ QTAILQ_FOREACH(cce, &pll->clock_change_handler, entry) {
+ cce->func(cce->opaque);
+ }
+
+ PRINT_DEBUG("%s rate: %llu\n", pll->name, pll->rate);
+
+ s->reg[I_(offset)] |= PLL_LOCKED_MASK;
+}
+
+
+static void exynos4210_cmu_set_rate(void *opaque, Exynos4210Clock clock_id)
+{
+ Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+ Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+ ClockChangeEntry *cce;
+
+ if (clock == NULL) {
+ hw_error("We haven't find source clock %d ", clock_id);
+ }
+
+ if ((clock->id == EXYNOS4210_MPLL) || (clock->id == EXYNOS4210_APLL)) {
+
+ exynos4210_cmu_set_pll(s, clock);
+
+ } else if ((clock->cmu_id != UNSPECIFIED_CMU)) {
+
+ Exynos4210ClockState *source;
+
+ uint32_t src_index = I_(clock->src_reg);
+ uint32_t div_index = I_(clock->div_reg);
+
+ clock->src_id = clock->src_ids[(s->reg[src_index] >>
+ clock->mux_shift) & 0xf];
+
+ source = exynos4210_clock_find(clock->src_id);
+ if (source == NULL) {
+ hw_error("We haven't find source clock %d (requested for %s)\n",
+ clock->src_id, clock->name);
+ }
+
+ clock->rate = muldiv64(source->rate, 1,
+ ((((clock->div_reg ? s->reg[div_index] : 0) >>
+ clock->div_shift) & 0xf) + 1));
+
+ QTAILQ_FOREACH(cce, &clock->clock_change_handler, entry) {
+ cce->func(cce->opaque);
+ }
+
+ PRINT_DEBUG_EXTEND("SRC: <0x%05x> %s, SHIFT: %d\n",
+ clock->src_reg,
+ exynos4210_cmu_regname(s, clock->src_reg),
+ clock->mux_shift);
+
+ PRINT_DEBUG("%s [%s:%llu]: %llu\n",
+ clock->name,
+ source->name,
+ (long long unsigned int)source->rate,
+ (long long unsigned int)clock->rate);
+ }
+}
+
+
+static uint64_t exynos4210_cmu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+
+ if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+ return 0;
+ }
+
+ if (offset & EXTENDED_REGION_MASK) {
+ if (s->cmu_id == EXYNOS4210_CMU_DMC) {
+ switch (offset & 0xFFF) {
+ case DCGIDX_MAP0:
+ case DCGIDX_MAP1:
+ case DCGIDX_MAP2:
+ case DCGPERF_MAP0:
+ case DCGPERF_MAP1:
+ case DVCIDX_MAP:
+ case FREQ_CPU:
+ case FREQ_DPM:
+ case DVSEMCLK_EN:
+ case MAXPERF:
+ return s->reg[I_(offset)];
+ default:
+ PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+ return 0;
+ }
+ }
+
+ if (s->cmu_id == EXYNOS4210_CMU_CPU) {
+ switch (offset & 0xFFF) {
+ case ARMCLK_STOPCTRL:
+ case ATCLK_STOPCTRL:
+ case PARITYFAIL_STATUS:
+ case PARITYFAIL_CLEAR:
+ case PWR_CTRL:
+ case APLL_CON0_L8:
+ case APLL_CON0_L7:
+ case APLL_CON0_L6:
+ case APLL_CON0_L5:
+ case APLL_CON0_L4:
+ case APLL_CON0_L3:
+ case APLL_CON0_L2:
+ case APLL_CON0_L1:
+ case IEM_CONTROL:
+ case APLL_CON1_L8:
+ case APLL_CON1_L7:
+ case APLL_CON1_L6:
+ case APLL_CON1_L5:
+ case APLL_CON1_L4:
+ case APLL_CON1_L3:
+ case APLL_CON1_L2:
+ case APLL_CON1_L1:
+ case CLKDIV_IEM_L8:
+ case CLKDIV_IEM_L7:
+ case CLKDIV_IEM_L6:
+ case CLKDIV_IEM_L5:
+ case CLKDIV_IEM_L4:
+ case CLKDIV_IEM_L3:
+ case CLKDIV_IEM_L2:
+ case CLKDIV_IEM_L1:
+ return s->reg[I_(offset)];
+ default:
+ PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+ return 0;
+ }
+ }
+ }
+
+ switch (offset & GROUP_MASK) {
+ case PLL_LOCK:
+ case PLL_CON:
+ case CLK_SRC:
+ case CLK_SRC_MASK:
+ case CLK_MUX_STAT:
+ case CLK_DIV:
+ case CLK_DIV_STAT:
+ case 0x700: /* Reserved */
+ case CLK_GATE_SCLK: /* reserved? */
+ case CLK_GATE_IP:
+ case CLKOUT_CMU:
+ return s->reg[I_(offset)];
+ default:
+ PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+ return 0;
+ }
+
+ PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n", offset,
+ exynos4210_cmu_regname(s, offset), s->reg[I_(offset)]);
+}
+
+
+static void exynos4210_cmu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+ uint32_t group, block;
+
+ group = offset & GROUP_MASK;
+ block = offset & BLOCK_MASK;
+
+ switch (group) {
+ case PLL_LOCK:
+ /* it's not necessary at this moment
+ * TODO: do it
+ */
+ break;
+ case PLL_CON:
+ switch (block) {
+ case APLL:
+ {
+ uint32_t pre_val = s->reg[I_(offset)];
+ s->reg[I_(offset)] = val;
+ val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+ s->reg[I_(offset)] = val;
+ exynos4210_cmu_set_rate(s, EXYNOS4210_APLL);
+ }
+ break;
+ case MPLL:
+ {
+ uint32_t pre_val = s->reg[I_(offset)];
+ s->reg[I_(offset)] = val;
+ val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+ s->reg[I_(offset)] = val;
+ exynos4210_cmu_set_rate(s, EXYNOS4210_MPLL);
+ }
+ break;
+ }
+ break;
+ case CLK_SRC:
+ switch (block) {
+ case CPU_BLK:
+ {
+ uint32_t pre_val = s->reg[I_(offset)];
+ s->reg[I_(offset)] = val;
+
+ if (val & MUX_APLL_SEL) {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(APLL_SEL_MASK)) |
+ (2 << APLL_SEL_SHIFT);
+
+ if ((pre_val & MUX_APLL_SEL) !=
+ (s->reg[I_(offset)] & MUX_APLL_SEL)) {
+ exynos4210_cmu_set_rate(s, EXYNOS4210_APLL);
+ }
+
+ } else {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(APLL_SEL_MASK)) |
+ (1 << APLL_SEL_SHIFT);
+
+ if ((pre_val & MUX_APLL_SEL) !=
+ (s->reg[I_(offset)] & MUX_APLL_SEL)) {
+ exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+ EXYNOS4210_XXTI);
+ }
+ }
+
+
+ if (val & MUX_MPLL_SEL) {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(MPLL_SEL_MASK)) |
+ (2 << MPLL_SEL_SHIFT);
+
+ if ((pre_val & MUX_MPLL_SEL) !=
+ (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
+ exynos4210_cmu_set_rate(s, EXYNOS4210_MPLL);
+ }
+
+ } else {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(MPLL_SEL_MASK)) |
+ (1 << MPLL_SEL_SHIFT);
+
+ if ((pre_val & MUX_MPLL_SEL) !=
+ (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
+ exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+ EXYNOS4210_XXTI);
+ }
+ }
+
+ if (val & MUX_CORE_SEL) {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(CORE_SEL_MASK)) |
+ (2 << CORE_SEL_SHIFT);
+
+ if ((pre_val & MUX_CORE_SEL) !=
+ (s->reg[I_(offset)] & MUX_CORE_SEL)) {
+ exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+ }
+
+ } else {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(CORE_SEL_MASK)) |
+ (1 << CORE_SEL_SHIFT);
+
+ if ((pre_val & MUX_CORE_SEL) !=
+ (s->reg[I_(offset)] & MUX_CORE_SEL)) {
+ exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+ EXYNOS4210_XXTI);
+ }
+ }
+
+ if (val & MUX_HPM_SEL) {
+ exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(HPM_SEL_MASK)) |
+ (2 << HPM_SEL_SHIFT);
+
+ if ((pre_val & MUX_HPM_SEL) !=
+ (s->reg[I_(offset)] & MUX_HPM_SEL)) {
+ exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+ }
+
+ } else {
+ s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+ (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(HPM_SEL_MASK)) |
+ (1 << HPM_SEL_SHIFT);
+
+ if ((pre_val & MUX_HPM_SEL) !=
+ (s->reg[I_(offset)] & MUX_HPM_SEL)) {
+ exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+ EXYNOS4210_XXTI);
+ }
+ }
+ }
+ break;
+ case TOP0_BLK:
+ s->reg[I_(offset)] = val;
+ exynos4210_cmu_set_rate(s, EXYNOS4210_ACLK_100);
+ break;
+ default:
+ PRINT_ERROR("Unknown functional block: 0x%x\n", (int)block);
+ }
+ break;
+ case CLK_SRC_MASK:
+ break;
+ case CLK_MUX_STAT:
+ break;
+ case CLK_DIV:
+ switch (block) {
+ case TOP_BLK:
+ s->reg[I_(offset)] = val;
+ exynos4210_cmu_set_rate(s, EXYNOS4210_ACLK_100);
+ break;
+ case CPU0_BLK:
+ s->reg[I_(offset)] = val;
+ exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_APLL);
+ exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+ break;
+ }
+ case CLK_DIV_STAT: /* CLK_DIV_STAT */
+ case 0x700: /* Reserved */
+ case CLK_GATE_SCLK: /* reserved? */
+ case CLK_GATE_IP:
+ case CLKOUT_CMU:
+ break;
+ default:
+ PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps exynos4210_cmu_ops = {
+ .read = exynos4210_cmu_read,
+ .write = exynos4210_cmu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void clock_rate_changed(void *opaque)
+{
+ Exynos4210ClockState *cs = (Exynos4210ClockState *)opaque;
+ Object *cmu = object_resolve_path(exynos4210_cmu_path[cs->cmu_id], NULL);
+ Exynos4210CmuState *s = OBJECT_CHECK(Exynos4210CmuState, cmu,
+ TYPE_EXYNOS4210_CMU);
+
+ PRINT_DEBUG("Clock %s was changed\n", cs->name);
+
+ exynos4210_cmu_set_rate(s, cs->id);
+
+}
+
+static void exynos4210_cmu_reset(DeviceState *dev)
+{
+ Exynos4210CmuState *s = OBJECT_CHECK(Exynos4210CmuState, OBJECT(dev),
+ TYPE_EXYNOS4210_CMU);
+ int i, j;
+ uint32_t index = 0;
+
+ for (i = 0; i < s->regs_number; i++) {
+ index = (s->regs[i].offset) / sizeof(uint32_t);
+ s->reg[index] = s->regs[i].reset_value;
+ }
+
+ for (i = 0; i < s->clock_number; i++) {
+
+ for (j = 0; j < SOURCES_NUMBER; j++) {
+
+ if (s->clock[i].src_ids[j] == UNSPECIFIED_CLOCK) {
+
+ if (j == 0) {
+ /*
+ * we have empty '.sources[]' array
+ */
+ if (s->clock[i].src_id != UNSPECIFIED_CLOCK) {
+
+ s->clock[i].src_ids[j] = s->clock[i].src_id;
+
+ } else {
+
+ if (s->clock[i].cmu_id != UNSPECIFIED_CMU) {
+ /*
+ * We haven't any defined sources for this clock.
+ * Error during definition of appropriate clock
+ * structure
+ */
+ hw_error("exynos4210_cmu_reset:"
+ "There aren't any sources for %s clock!\n",
+ s->clock[i].name);
+ } else {
+ /*
+ * we don't need any sources for this clock
+ * because it's a root clock
+ */
+ break;
+ }
+ }
+ } else {
+ break; /* leave because there are no more sources */
+ }
+ } /* src_ids[j] == UNSPECIFIED_CLOCK */
+
+ Exynos4210ClockState *source =
+ exynos4210_clock_find(s->clock[i].src_ids[j]);
+
+ if (source == NULL) {
+ hw_error("We aren't be able to find source clock %d "
+ "(requested for %s)\n",
+ s->clock[i].src_ids[j], s->clock[i].name);
+ }
+
+ if (source->cmu_id != UNSPECIFIED_CMU) {
+
+ exynos4210_register_clock_handler(clock_rate_changed,
+ s->clock[i].src_ids[j], &s->clock[i]);
+ }
+ } /* SOURCES_NUMBER */
+
+ exynos4210_cmu_set_rate(s, s->clock[i].id);
+ }
+
+ PRINT_DEBUG("CMU %d reset completed\n", s->cmu_id);
+}
+
+
+static const VMStateDescription vmstate_exynos4210_cmu = {
+ .name = TYPE_EXYNOS4210_CMU,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ /*
+ * TODO: Maybe we should save Exynos4210ClockState structs as well
+ */
+ VMSTATE_UINT32_ARRAY(reg, Exynos4210CmuState,
+ EXYNOS4210_CMU_REGS_MEM_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+DeviceState *exynos4210_cmu_create(target_phys_addr_t addr,
+ Exynos4210Cmu cmu_id)
+{
+ DeviceState *dev;
+ SysBusDevice *bus;
+
+ dev = qdev_create(NULL, TYPE_EXYNOS4210_CMU);
+
+ qdev_prop_set_int32(dev, "cmu_id", cmu_id);
+
+ bus = sysbus_from_qdev(dev);
+ qdev_init_nofail(dev);
+ if (addr != (target_phys_addr_t)-1) {
+ sysbus_mmio_map(bus, 0, addr);
+ }
+
+ return dev;
+}
+
+static int exynos4210_cmu_init(SysBusDevice *dev)
+{
+ Exynos4210CmuState *s = FROM_SYSBUS(Exynos4210CmuState, dev);
+ int i, n;
+
+ memory_region_init_io(&s->iomem, &exynos4210_cmu_ops, s,
+ TYPE_EXYNOS4210_CMU, EXYNOS4210_CMU_REGS_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ switch (s->cmu_id) {
+ case EXYNOS4210_CMU_LEFTBUS:
+ s->regs = exynos4210_cmu_leftbus_regs;
+ s->regs_number = ARRAY_SIZE(exynos4210_cmu_leftbus_regs);
+ break;
+ case EXYNOS4210_CMU_RIGHTBUS:
+ s->regs = exynos4210_cmu_rightbus_regs;
+ s->regs_number = ARRAY_SIZE(exynos4210_cmu_rightbus_regs);
+ break;
+ case EXYNOS4210_CMU_TOP:
+ s->regs = exynos4210_cmu_top_regs;
+ s->regs_number = ARRAY_SIZE(exynos4210_cmu_top_regs);
+ break;
+ case EXYNOS4210_CMU_DMC:
+ s->regs = exynos4210_cmu_dmc_regs;
+ s->regs_number = ARRAY_SIZE(exynos4210_cmu_dmc_regs);
+ break;
+ case EXYNOS4210_CMU_CPU:
+ s->regs = exynos4210_cmu_cpu_regs;
+ s->regs_number = ARRAY_SIZE(exynos4210_cmu_cpu_regs);
+ break;
+ default:
+ hw_error("Wrong CMU: %d\n", s->cmu_id);
+ }
+
+ for (i = 1, n = 0; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+ if (s->cmu_id == exynos4210_clock[i]->cmu_id) {
+ n++;
+ }
+ }
+
+ s->clock =
+ (Exynos4210ClockState *)g_malloc0(n * sizeof(Exynos4210ClockState));
+
+ for (i = 1, s->clock_number = 0; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+
+ if (s->cmu_id == exynos4210_clock[i]->cmu_id) {
+
+ memcpy(&s->clock[s->clock_number], exynos4210_clock[i],
+ sizeof(Exynos4210ClockState));
+
+ QTAILQ_INIT(&s->clock[s->clock_number].clock_change_handler);
+
+ PRINT_DEBUG("Clock %s was added to \"%s\"\n",
+ s->clock[s->clock_number].name,
+ exynos4210_cmu_path[s->cmu_id]);
+
+ s->clock_number++;
+
+ }
+ }
+
+ object_property_add_child(object_get_root(), exynos4210_cmu_path[s->cmu_id],
+ OBJECT(dev), NULL);
+
+ return 0;
+}
+
+static Property exynos4210_cmu_properties[] = {
+ DEFINE_PROP_INT32("cmu_id", Exynos4210CmuState, cmu_id, UNSPECIFIED_CMU),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_cmu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_cmu_init;
+ dc->reset = exynos4210_cmu_reset;
+ dc->props = exynos4210_cmu_properties;
+ dc->vmsd = &vmstate_exynos4210_cmu;
+}
+
+static TypeInfo exynos4210_cmu_info = {
+ .name = TYPE_EXYNOS4210_CMU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210CmuState),
+ .class_init = exynos4210_cmu_class_init,
+};
+
+static void exynos4210_cmu_register_types(void)
+{
+ type_register_static(&exynos4210_cmu_info);
+}
+
+type_init(exynos4210_cmu_register_types)
--- /dev/null
+/*
+ * Samsung exynos4210 MALI400 gpu (G3D) emulation
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Mitsyanko Igor <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "sysbus.h"
+
+/* Debug messages configuration */
+#define EXY_G3D_DEBUG 0
+
+#if EXY_G3D_DEBUG == 0
+ #define DPRINT_L1(fmt, args...) do { } while (0)
+ #define DPRINT_L2(fmt, args...) do { } while (0)
+ #define DPRINT_ERROR(fmt, args...) do { } while (0)
+#elif EXY_G3D_DEBUG == 1
+ #define DPRINT_L1(fmt, args...) \
+ do {fprintf(stderr, "QEMU G3D: "fmt, ## args); } while (0)
+ #define DPRINT_L2(fmt, args...) do { } while (0)
+ #define DPRINT_ERROR(fmt, args...) \
+ do {fprintf(stderr, "QEMU G3D ERROR: "fmt, ## args); } while (0)
+#else
+ #define DPRINT_L1(fmt, args...) \
+ do {fprintf(stderr, "QEMU G3D: "fmt, ## args); } while (0)
+ #define DPRINT_L2(fmt, args...) \
+ do {fprintf(stderr, "QEMU G3D: "fmt, ## args); } while (0)
+ #define DPRINT_ERROR(fmt, args...) \
+ do {fprintf(stderr, "QEMU G3D ERROR: "fmt, ## args); } while (0)
+#endif
+
+#define NUM_OF_PIXPROC 4
+#define EXYNOS4210_G3D_REG_MEM_SIZE 0x10000
+#define MALIGP_REGS_SIZE 0x98
+#define MALIPP_REGS_SIZE 0x10f0
+#define MALIMMU_REGS_SIZE 0x24
+#define MALI_L2CACHE_REGS_SIZE 0x30
+
+#define GP_OFF_START 0x0000
+#define GP_OFF_END (GP_OFF_START + MALIGP_REGS_SIZE)
+#define L2_OFF_START 0x1000
+#define L2_OFF_END (L2_OFF_START + MALI_L2CACHE_REGS_SIZE)
+#define PMU_OFF_START 0x2000
+#define PMU_OFF_END 0x2FFC
+#define GP_MMU_OFF_START 0x3000
+#define GP_MMU_OFF_END (GP_MMU_OFF_START + MALIMMU_REGS_SIZE)
+#define PP0_MMU_OFF_START 0x4000
+#define PP0_MMU_OFF_END (PP0_MMU_OFF_START + MALIMMU_REGS_SIZE)
+#define PP1_MMU_OFF_START 0x5000
+#define PP1_MMU_OFF_END (PP1_MMU_OFF_START + MALIMMU_REGS_SIZE)
+#define PP2_MMU_OFF_START 0x6000
+#define PP2_MMU_OFF_END (PP2_MMU_OFF_START + MALIMMU_REGS_SIZE)
+#define PP3_MMU_OFF_START 0x7000
+#define PP3_MMU_OFF_END (PP3_MMU_OFF_START + MALIMMU_REGS_SIZE)
+#define PP0_OFF_START 0x8000
+#define PP0_OFF_END (PP0_OFF_START + MALIPP_REGS_SIZE)
+#define PP1_OFF_START 0xA000
+#define PP1_OFF_END (PP1_OFF_START + MALIPP_REGS_SIZE)
+#define PP2_OFF_START 0xC000
+#define PP2_OFF_END (PP2_OFF_START + MALIPP_REGS_SIZE)
+#define PP3_OFF_START 0xE000
+#define PP3_OFF_END (PP3_OFF_START + MALIPP_REGS_SIZE)
+
+/**************************************************
+ * MALI geometry processor register defines
+ **************************************************/
+
+#define MALIGP_REG_OFF_VSCL_START_ADDR 0x00
+#define MALIGP_REG_OFF_VSCL_END_ADDR 0x04
+#define MALIGP_REG_OFF_PLBUCL_START_ADDR 0x08
+#define MALIGP_REG_OFF_PLBUCL_END_ADDR 0x0c
+#define MALIGP_REG_OFF_PLBU_ALLOC_START_ADDR 0x10
+#define MALIGP_REG_OFF_PLBU_ALLOC_END_ADDR 0x14
+
+/* Command to geometry processor */
+#define MALIGP_REG_OFF_CMD 0x20
+#define MALIGP_REG_VAL_CMD_START_VS (1 << 0)
+#define MALIGP_REG_VAL_CMD_START_PLBU (1 << 1)
+#define MALIGP_REG_VAL_CMD_UPDATE_PLBU_ALLOC (1 << 4)
+#define MALIGP_REG_VAL_CMD_RESET (1 << 5)
+#define MALIGP_REG_VAL_CMD_FORCE_HANG (1 << 6)
+#define MALIGP_REG_VAL_CMD_STOP_BUS (1 << 9)
+#define MALIGP_REG_VAL_CMD_SOFT_RESET (1 << 10)
+
+/* Raw interrupt ststus - can not be masked by INT_MASK */
+#define MALIGP_REG_OFF_INT_RAWSTAT 0x24
+/* WO register - write 1 to clear irq flag in INT_RAWSTAT */
+#define MALIGP_REG_OFF_INT_CLEAR 0x28
+/* MASK interrupt requests in IRQ_STATUS */
+#define MALIGP_REG_OFF_INT_MASK 0x2C
+/* Bits are set only if not masked and if bit in RAWSTAT is set */
+#define MALIGP_REG_OFF_INT_STAT 0x30
+/* Interrupt statuses of geometry processor */
+#define MALIGP_REG_VAL_IRQ_VS_END_CMD_LST (1 << 0)
+#define MALIGP_REG_VAL_IRQ_PLBU_END_CMD_LST (1 << 1)
+#define MALIGP_REG_VAL_IRQ_PLBU_OUT_OF_MEM (1 << 2)
+#define MALIGP_REG_VAL_IRQ_VS_SEM_IRQ (1 << 3)
+#define MALIGP_REG_VAL_IRQ_PLBU_SEM_IRQ (1 << 4)
+#define MALIGP_REG_VAL_IRQ_HANG (1 << 5)
+#define MALIGP_REG_VAL_IRQ_FORCE_HANG (1 << 6)
+#define MALIGP_REG_VAL_IRQ_PERF_CNT_0_LIMIT (1 << 7)
+#define MALIGP_REG_VAL_IRQ_PERF_CNT_1_LIMIT (1 << 8)
+#define MALIGP_REG_VAL_IRQ_WRITE_BOUND_ERR (1 << 9)
+#define MALIGP_REG_VAL_IRQ_SYNC_ERROR (1 << 10)
+#define MALIGP_REG_VAL_IRQ_AXI_BUS_ERROR (1 << 11)
+#define MALIGP_REG_VAL_IRQ_AXI_BUS_STOPPED (1 << 12)
+#define MALIGP_REG_VAL_IRQ_VS_INVALID_CMD (1 << 13)
+#define MALIGP_REG_VAL_IRQ_PLB_INVALID_CMD (1 << 14)
+#define MALIGP_REG_VAL_IRQ_RESET_COMPLETED (1 << 19)
+#define MALIGP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW (1 << 20)
+#define MALIGP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW (1 << 21)
+#define MALIGP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS (1 << 22)
+#define MALIGP_REG_VAL_IRQ_MASK_ALL \
+ (\
+ MALIGP_REG_VAL_IRQ_VS_END_CMD_LST | \
+ MALIGP_REG_VAL_IRQ_PLBU_END_CMD_LST | \
+ MALIGP_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \
+ MALIGP_REG_VAL_IRQ_VS_SEM_IRQ | \
+ MALIGP_REG_VAL_IRQ_PLBU_SEM_IRQ | \
+ MALIGP_REG_VAL_IRQ_HANG | \
+ MALIGP_REG_VAL_IRQ_FORCE_HANG | \
+ MALIGP_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \
+ MALIGP_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \
+ MALIGP_REG_VAL_IRQ_WRITE_BOUND_ERR | \
+ MALIGP_REG_VAL_IRQ_SYNC_ERROR | \
+ MALIGP_REG_VAL_IRQ_AXI_BUS_ERROR | \
+ MALIGP_REG_VAL_IRQ_AXI_BUS_STOPPED | \
+ MALIGP_REG_VAL_IRQ_VS_INVALID_CMD | \
+ MALIGP_REG_VAL_IRQ_PLB_INVALID_CMD | \
+ MALIGP_REG_VAL_IRQ_RESET_COMPLETED | \
+ MALIGP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \
+ MALIGP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \
+ MALIGP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS)
+
+
+#define MALIGP_REG_OFF_WRITE_BOUND_LOW 0x34
+#define MALIGP_REG_OFF_PERF_CNT_0_ENABLE 0x3C
+#define MALIGP_REG_OFF_PERF_CNT_1_ENABLE 0x40
+#define MALIGP_REG_OFF_PERF_CNT_0_SRC 0x44
+#define MALIGP_REG_OFF_PERF_CNT_1_SRC 0x48
+#define MALIGP_REG_OFF_PERF_CNT_0_VALUE 0x4C
+#define MALIGP_REG_OFF_PERF_CNT_1_VALUE 0x50
+#define MALIGP_REG_OFF_STATUS 0x68
+
+/* Geometry processor version register */
+#define MALIGP_REG_OFF_VERSION 0x6C
+#define MALI400_GP_PRODUCT_ID 0xB07
+#define MALIGP_VERSION_DEFAULT (MALI400_GP_PRODUCT_ID << 16)
+
+#define MALIGP_REG_OFF_VSCL_START_ADDR_READ 0x80
+#define MALIGP_REG_OFF_PLBCL_START_ADDR_READ 0x84
+#define MALIGP_CONTR_AXI_BUS_ERROR_STAT 0x94
+#define MALIGP_REGISTER_ADDRESS_SPACE_SIZE 0x98
+
+
+/**************************************************
+ * MALI pixel processor registers defines
+ **************************************************/
+
+/* MALI Pixel Processor version register */
+#define MALIPP_REG_OFF_VERSION 0x1000
+#define MALI400_PP_PRODUCT_ID 0xCD07
+#define MALIPP_MGMT_VERSION_DEFAULT (MALI400_PP_PRODUCT_ID << 16)
+
+#define MALIPP_REG_OFF_CURRENT_REND_LIST_ADDR 0x1004
+#define MALIPP_REG_OFF_STATUS 0x1008
+
+/* Control and commands register */
+#define MALIPP_REG_OFF_CTRL_MGMT 0x100c
+#define MALIPP_REG_VAL_CTRL_MGMT_STOP_BUS (1 << 0)
+#define MALIPP_REG_VAL_CTRL_MGMT_FORCE_RESET (1 << 5)
+#define MALIPP_REG_VAL_CTRL_MGMT_START_RENDERING (1 << 6)
+#define MALIPP_REG_VAL_CTRL_MGMT_SOFT_RESET (1 << 7)
+
+/* Interrupt control registers */
+#define MALIPP_REG_OFF_INT_RAWSTAT 0x1020
+#define MALIPP_REG_OFF_INT_CLEAR 0x1024
+#define MALIPP_REG_OFF_INT_MASK 0x1028
+#define MALIPP_REG_OFF_INT_STATUS 0x102c
+/* Interrupt statuses */
+#define MALIPP_REG_VAL_IRQ_END_OF_FRAME (1 << 0)
+#define MALIPP_REG_VAL_IRQ_END_OF_TILE (1 << 1)
+#define MALIPP_REG_VAL_IRQ_HANG (1 << 2)
+#define MALIPP_REG_VAL_IRQ_FORCE_HANG (1 << 3)
+#define MALIPP_REG_VAL_IRQ_BUS_ERROR (1 << 4)
+#define MALIPP_REG_VAL_IRQ_BUS_STOP (1 << 5)
+#define MALIPP_REG_VAL_IRQ_CNT_0_LIMIT (1 << 6)
+#define MALIPP_REG_VAL_IRQ_CNT_1_LIMIT (1 << 7)
+#define MALIPP_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR (1 << 8)
+#define MALIPP_REG_VAL_IRQ_INVALID_PLIST_COMMAND (1 << 9)
+#define MALIPP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW (1 << 10)
+#define MALIPP_REG_VAL_IRQ_CALL_STACK_OVERFLOW (1 << 11)
+#define MALIPP_REG_VAL_IRQ_RESET_COMPLETED (1 << 12)
+
+
+#define MALIPP_REG_OFF_WRITE_BOUNDARY_LOW 0x1044
+#define MALIPP_REG_OFF_BUS_ERROR_STATUS 0x1050
+#define MALIPP_REG_OFF_PERF_CNT_0_ENABLE 0x1080
+#define MALIPP_REG_OFF_PERF_CNT_0_SRC 0x1084
+#define MALIPP_REG_OFF_PERF_CNT_0_VALUE 0x108c
+#define MALIPP_REG_OFF_PERF_CNT_1_ENABLE 0x10a0
+#define MALIPP_REG_OFF_PERF_CNT_1_SRC 0x10a4
+#define MALIPP_REG_OFF_PERF_CNT_1_VALUE 0x10ac
+#define MALIPP_REG_SIZEOF_REGISTER_BANK 0x10f0
+
+/**************************************************
+ * MALI MMU register defines
+ **************************************************/
+
+/* Current Page Directory Pointer */
+#define MALI_MMU_REG_DTE_ADDR 0x0000
+
+/* Status of the MMU */
+#define MALI_MMU_REG_STATUS 0x0004
+/* MALI MMU ststus bits */
+#define MALI_MMU_STATUS_PAGING_ENABLED (1 << 0)
+#define MALI_MMU_STATUS_PAGE_FAULT_ACTIVE (1 << 1)
+#define MALI_MMU_STATUS_STALL_ACTIVE (1 << 2)
+#define MALI_MMU_STATUS_IDLE (1 << 3)
+#define MALI_MMU_STATUS_REPLAY_BUFFER_EMPTY (1 << 4)
+#define MALI_MMU_STATUS_PAGE_FAULT_IS_WRITE (1 << 5)
+
+/* Command register, used to control the MMU */
+#define MALI_MMU_REG_COMMAND 0x0008
+/* MALI MMU commands */
+/* Enable paging (memory translation) */
+#define MALI_MMU_COMMAND_ENABLE_PAGING 0x00
+/* Disable paging (memory translation) */
+#define MALI_MMU_COMMAND_DISABLE_PAGING 0x01
+/* Enable stall on page fault */
+#define MALI_MMU_COMMAND_ENABLE_STALL 0x02
+/* Disable stall on page fault */
+#define MALI_MMU_COMMAND_DISABLE_STALL 0x03
+/* Zap the entire page table cache */
+#define MALI_MMU_COMMAND_ZAP_CACHE 0x04
+/* Page fault processed */
+#define MALI_MMU_COMMAND_PAGE_FAULT_DONE 0x05
+/* Reset the MMU back to power-on settings */
+#define MALI_MMU_COMMAND_SOFT_RESET 0x06
+
+/* Logical address of the last page fault */
+#define MALI_MMU_REG_PAGE_FAULT_ADDR 0x000C
+
+/* Used to invalidate the mapping of a single page from the MMU */
+#define MALI_MMU_REG_ZAP_ONE_LINE 0x0010
+
+/* Raw interrupt status, all interrupts visible */
+#define MALI_MMU_REG_INT_RAWSTAT 0x0014
+/* Indicate to the MMU that the interrupt has been received */
+#define MALI_MMU_REG_INT_CLEAR 0x0018
+/* Enable/disable types of interrupts */
+#define MALI_MMU_REG_INT_MASK 0x001C
+/* Interrupt status based on the mask */
+#define MALI_MMU_REG_INT_STATUS 0x0020
+/* MALI MMU interrupt registers bits */
+/* A page fault occured */
+#define MALI_MMU_INT_PAGE_FAULT 0x01
+/* A bus read error occured */
+#define MALI_MMU_INT_READ_BUS_ERROR 0x02
+
+
+/**************************************************
+ * MALI L2 cache register defines
+ **************************************************/
+
+/* MALI L2 cache status bits */
+#define MALI_L2CACHE_REG_STATUS 0x0008
+/* Command handler of L2 cache is busy */
+#define MALI_L2CACHE_STATUS_COMMAND_BUSY 0x01
+/* L2 cache is busy handling data requests */
+#define MALI_L2CACHE_STATUS_DATA_BUSY 0x02
+
+/* Misc cache commands, e.g. clear */
+#define MALI_L2CACHE_REG_COMMAND 0x0010
+/* Clear the entire cache */
+#define MALI_L2CACHE_CMD_CLEAR_ALL 0x01
+
+#define MALI_L2CACHE_REG_CLEAR_PAGE 0x0014
+
+/* Enable misc cache features */
+#define MALI_L2CACHE_REG_ENABLE 0x001C
+/* Default state of enable register */
+#define MALI_L2CACHE_ENABLE_DEFAULT 0x0
+/* Permit cacheable accesses */
+#define MALI_L2CACHE_ENABLE_ACCESS 0x01
+/* Permit cache read allocate */
+#define MALI_L2CACHE_ENABLE_READ_ALLOCATE 0x02
+
+#define MALI_L2CACHE_REG_PERFCNT_SRC0 0x0020
+#define MALI_L2CACHE_REG_PERFCNT_VAL0 0x0024
+#define MALI_L2CACHE_REG_PERFCNT_SRC1 0x0028
+#define MALI_L2CACHE_REG_PERFCNT_VAL1 0x002C
+
+typedef struct GeometryProc {
+ uint32_t vscl_start;
+ uint32_t vscl_end;
+ uint32_t plbucl_start;
+ uint32_t plbucl_end;
+ uint32_t plbu_alloc_start;
+ uint32_t plbu_alloc_end;
+ uint32_t cmd;
+ uint32_t int_rawstat;
+/* uint32_t int_clear; Write only register */
+ uint32_t int_mask;
+ uint32_t int_stat;
+ uint32_t write_bound;
+ uint32_t perfcnt0_en;
+ uint32_t perfcnt1_en;
+ uint32_t perfcnt0_src;
+ uint32_t perfcnt1_src;
+ uint32_t perfcnt0_value;
+ uint32_t perfcnt1_value;
+ uint32_t status;
+/* uint32_t version; RO register */
+ uint32_t vscl_start_read;
+ uint32_t plbcl_start_read;
+ uint32_t axi_error;
+ uint32_t addr_space_size;
+
+ qemu_irq irq_gp; /* geometry processor interrupt */
+} GeometryProc;
+
+typedef struct PixelProc {
+/* uint32_t version; RO register */
+ uint32_t cur_rend_list;
+ uint32_t status;
+ uint32_t ctrl_mgmt;
+ uint32_t int_rawstat;
+/* uint32_t int_clear; Write only register */
+ uint32_t int_mask;
+ uint32_t int_stat;
+ uint32_t write_bound;
+ uint32_t bus_error;
+ uint32_t perfcnt0_en;
+ uint32_t perfcnt0_src;
+ uint32_t perfcnt0_value;
+ uint32_t perfcnt1_en;
+ uint32_t perfcnt1_src;
+ uint32_t perfcnt1_value;
+ uint32_t sizeof_regs;
+
+ qemu_irq irq_pp; /* pixel processor interrupt */
+} PixelProc;
+
+typedef struct MaliMMU {
+ uint32_t dte_addr;
+ uint32_t status;
+ uint32_t command;
+ uint32_t page_fault_addr;
+ uint32_t zap_one_line;
+ uint32_t int_rawstat;
+ uint32_t int_mask;
+ uint32_t int_status;
+
+ qemu_irq irq_mmu; /* MALI MMU interrupt */
+} MaliMMU;
+
+typedef struct MaliL2Cache {
+ uint32_t status;
+ uint32_t command;
+ uint32_t clear_page;
+ uint32_t enable;
+ uint32_t perfcnt_src0;
+ uint32_t perfcnt_val0;
+ uint32_t perfcnt_src1;
+ uint32_t perfcnt_val1;
+} MaliL2Cache;
+
+typedef struct Exynos4210G3DState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ GeometryProc gp; /* Geometry processor */
+ PixelProc pp[NUM_OF_PIXPROC];
+ MaliMMU gp_mmu;
+ MaliMMU pp_mmu[NUM_OF_PIXPROC];
+ MaliL2Cache l2_cache;
+ qemu_irq irq_pmu; /* power unit interrupt */
+} Exynos4210G3DState;
+
+/* TODO maybe reset functions must do something else besides clearing reg-s */
+static inline void exynos4210_maligp_reset(GeometryProc *gp)
+{
+ memset(gp, 0, offsetof(GeometryProc, irq_gp));
+}
+
+static inline void exynos4210_malipp_reset(PixelProc *pp)
+{
+ memset(pp, 0, offsetof(PixelProc, irq_pp));
+}
+
+static inline void exynos4210_malimmu_reset(MaliMMU *mmu)
+{
+ memset(mmu, 0, offsetof(MaliMMU, irq_mmu));
+}
+
+static inline void exynos4210_mali_l2cache_reset(MaliL2Cache *l2cach)
+{
+ memset(l2cach, 0, sizeof(MaliL2Cache));
+}
+
+static void exynos4210_g3d_reset(DeviceState *d)
+{
+ Exynos4210G3DState *s = DO_UPCAST(Exynos4210G3DState, busdev.qdev, d);
+ int i;
+
+ exynos4210_maligp_reset(&s->gp);
+ exynos4210_malimmu_reset(&s->gp_mmu);
+ exynos4210_mali_l2cache_reset(&s->l2_cache);
+ for (i = 0; i < NUM_OF_PIXPROC; i++) {
+ exynos4210_malipp_reset(&s->pp[i]);
+ exynos4210_malimmu_reset(&s->pp_mmu[i]);
+ }
+}
+
+/* Geometry processor register map read */
+static inline uint32_t exynos4210_maligp_read(GeometryProc *gp,
+ target_phys_addr_t offset)
+{
+ DPRINT_L2("MALI GEOMETRY PROCESSOR: read offset 0x"
+ TARGET_FMT_plx "\n", offset);
+
+ switch (offset) {
+ /* Geometry processor */
+ case MALIGP_REG_OFF_CMD:
+ return gp->cmd;
+ case MALIGP_REG_OFF_INT_RAWSTAT:
+ return gp->int_rawstat;
+ case MALIGP_REG_OFF_INT_MASK:
+ return gp->int_mask;
+ case MALIGP_REG_OFF_INT_STAT:
+ return gp->int_stat;
+ case MALIGP_REG_OFF_VERSION:
+ return MALIGP_VERSION_DEFAULT;
+ case MALIGP_REG_OFF_INT_CLEAR:
+ DPRINT_L1("MALI GEOM PROC: read from WO register! Offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ default:
+ DPRINT_L1("MALI GEOM PROC: read from non-existing register! "
+ "Offset 0x" TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+/* Geometry processor register map write */
+static uint32_t exynos4210_malipp_read(PixelProc *pp, target_phys_addr_t offset)
+{
+ DPRINT_L2("read offset 0x" TARGET_FMT_plx "\n", offset);
+
+ switch (offset) {
+ case MALIPP_REG_OFF_VERSION:
+ return MALIPP_MGMT_VERSION_DEFAULT;
+ case MALIPP_REG_OFF_CTRL_MGMT:
+ return pp->ctrl_mgmt;
+ case MALIPP_REG_OFF_INT_RAWSTAT:
+ return pp->int_rawstat;
+ case MALIPP_REG_OFF_INT_MASK:
+ return pp->int_mask;
+ case MALIPP_REG_OFF_INT_STATUS:
+ return pp->int_stat;
+ case MALIPP_REG_OFF_INT_CLEAR:
+ DPRINT_L1("MALI PIX PROC: read from WO register! Offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ default:
+ DPRINT_L1("MALI PIX PROC: read from non-existing register! Offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+/* MALI MMU register map read */
+static uint32_t exynos4210_malimmu_read(MaliMMU *mmu, target_phys_addr_t offset)
+{
+ DPRINT_L2("read offset 0x" TARGET_FMT_plx "\n", offset);
+
+ switch (offset) {
+ case MALI_MMU_REG_DTE_ADDR:
+ return mmu->dte_addr;
+ case MALI_MMU_REG_COMMAND:
+ return mmu->command;
+ case MALI_MMU_REG_INT_RAWSTAT:
+ return mmu->int_rawstat;
+ case MALI_MMU_REG_INT_MASK:
+ return mmu->int_mask;
+ case MALI_MMU_REG_INT_STATUS:
+ return mmu->int_status;
+ case MALI_MMU_REG_INT_CLEAR:
+ DPRINT_L1("MALI MMU: read from WO register! Offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ default:
+ DPRINT_L1("MALI MMU: read from non-existing register! Offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+/* MALI L2 cache register map read */
+static uint32_t exynos4210_mali_l2cache_read(MaliL2Cache *cache,
+ target_phys_addr_t offset)
+{
+ DPRINT_L2("MALI L2 CACHE: read offset 0x"
+ TARGET_FMT_plx "\n", offset);
+
+ switch (offset) {
+ case MALI_L2CACHE_REG_STATUS:
+ return cache->status;
+ case MALI_L2CACHE_REG_COMMAND:
+ return cache->command;
+ case MALI_L2CACHE_REG_ENABLE:
+ return cache->enable;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t exynos4210_g3d_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210G3DState *s = (Exynos4210G3DState *)opaque;
+
+ switch (offset) {
+ case GP_OFF_START ... GP_OFF_END:
+ return exynos4210_maligp_read(&s->gp, offset - GP_OFF_START);
+ case PP0_OFF_START ... PP0_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#0: ");
+ return exynos4210_malipp_read(&s->pp[0], offset - PP0_OFF_START);
+ case PP1_OFF_START ... PP1_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#1: ");
+ return exynos4210_malipp_read(&s->pp[1], offset - PP1_OFF_START);
+ case PP2_OFF_START ... PP2_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#2: ");
+ return exynos4210_malipp_read(&s->pp[2], offset - PP2_OFF_START);
+ case PP3_OFF_START ... PP3_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#3: ");
+ return exynos4210_malipp_read(&s->pp[3], offset - PP3_OFF_START);
+ case GP_MMU_OFF_START ... GP_MMU_OFF_END:
+ DPRINT_L2("MALI GEOM PROC MMU: ");
+ return exynos4210_malimmu_read(&s->gp_mmu, offset - GP_MMU_OFF_START);
+ case PP0_MMU_OFF_START ... PP0_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#0 MMU: ");
+ return exynos4210_malimmu_read(&s->pp_mmu[0],
+ offset - PP0_MMU_OFF_START);
+ case PP1_MMU_OFF_START ... PP1_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#1 MMU: ");
+ return exynos4210_malimmu_read(&s->pp_mmu[1],
+ offset - PP1_MMU_OFF_START);
+ case PP2_MMU_OFF_START ... PP2_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#2 MMU: ");
+ return exynos4210_malimmu_read(&s->pp_mmu[2],
+ offset - PP2_MMU_OFF_START);
+ case PP3_MMU_OFF_START ... PP3_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#3 MMU: ");
+ return exynos4210_malimmu_read(&s->pp_mmu[3],
+ offset - PP3_MMU_OFF_START);
+ case L2_OFF_START ... L2_OFF_END:
+ return exynos4210_mali_l2cache_read(&s->l2_cache,
+ offset - L2_OFF_START);
+ default:
+ DPRINT_L1("MALI UNKNOWN REGION: read offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+/* Geometry processor register map write */
+static void exynos4210_maligp_write(GeometryProc *gp,
+ target_phys_addr_t offset, uint32_t val)
+{
+ DPRINT_L2("MALI GEOMETRY PROCESSOR: write offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+
+ switch (offset) {
+ case MALIGP_REG_OFF_CMD:
+ gp->cmd = val;
+ if (val & MALIGP_REG_VAL_CMD_SOFT_RESET) {
+ exynos4210_maligp_reset(gp);
+ gp->int_rawstat |= MALIGP_REG_VAL_IRQ_RESET_COMPLETED;
+ gp->int_stat = gp->int_rawstat & gp->int_mask;
+ qemu_set_irq(gp->irq_gp, gp->int_stat);
+ }
+ break;
+ case MALIGP_REG_OFF_INT_CLEAR:
+ gp->int_rawstat &= ~val;
+ gp->int_stat = gp->int_rawstat & gp->int_mask;
+ qemu_irq_lower(gp->irq_gp);
+ break;
+ case MALIGP_REG_OFF_INT_MASK:
+ gp->int_mask = val;
+ break;
+ case MALIGP_REG_OFF_VERSION: case MALIGP_REG_OFF_INT_RAWSTAT:
+ case MALIGP_REG_OFF_INT_STAT:
+ DPRINT_L1("MALI GEOM PROC: writing to RO register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ default:
+ DPRINT_L1("MALI GEOM PROC: writing to non-existing register! "
+ "Offset 0x" TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ }
+}
+
+/* Pixel processor register map write */
+static void exynos4210_malipp_write(PixelProc *pp,
+ target_phys_addr_t offset, uint32_t val)
+{
+ DPRINT_L2("write offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+
+ switch (offset) {
+ case MALIPP_REG_OFF_CTRL_MGMT:
+ pp->ctrl_mgmt = val;
+ if (val & MALIPP_REG_VAL_CTRL_MGMT_SOFT_RESET) {
+ exynos4210_malipp_reset(pp);
+ pp->int_rawstat |= MALIPP_REG_VAL_IRQ_RESET_COMPLETED;
+ pp->int_stat = pp->int_rawstat & pp->int_mask;
+ qemu_set_irq(pp->irq_pp, pp->int_stat);
+ }
+ break;
+ case MALIPP_REG_OFF_INT_CLEAR:
+ pp->int_rawstat &= ~val;
+ pp->int_stat = pp->int_rawstat & pp->int_mask;
+ qemu_irq_lower(pp->irq_pp);
+ break;
+ case MALIPP_REG_OFF_INT_MASK:
+ pp->int_mask = val;
+ break;
+ case MALIPP_REG_OFF_INT_STATUS: case MALIPP_REG_OFF_VERSION:
+ case MALIPP_REG_OFF_INT_RAWSTAT:
+ DPRINT_L1("MALI PIX PROC: writing to RO register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ default:
+ DPRINT_L1("MALI PIX PROC: writing to non-existing register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ }
+}
+
+/* MALI MMU register map write */
+static void exynos4210_malimmu_write(MaliMMU *mmu,
+ target_phys_addr_t offset, uint32_t val)
+{
+ DPRINT_L2("write offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+
+ switch (offset) {
+ case MALI_MMU_REG_DTE_ADDR:
+ mmu->dte_addr = val;
+ break;
+ case MALI_MMU_REG_COMMAND:
+ mmu->command = val;
+ if (val & MALI_MMU_COMMAND_SOFT_RESET) {
+ exynos4210_malimmu_reset(mmu);
+ }
+ break;
+ case MALI_MMU_REG_INT_CLEAR:
+ mmu->int_rawstat &= ~val;
+ mmu->int_status = mmu->int_rawstat & mmu->int_mask;
+ qemu_irq_lower(mmu->irq_mmu);
+ break;
+ case MALI_MMU_REG_INT_MASK:
+ mmu->int_mask = val;
+ break;
+ case MALI_MMU_REG_INT_STATUS: case MALI_MMU_REG_INT_RAWSTAT:
+ DPRINT_L1("MALI MMU: writing to RO register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ default:
+ DPRINT_L1("MALI MMU: writing to non-existing register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ }
+}
+
+/* MALI L2 cache register map write */
+static void exynos4210_mali_l2cache_write(MaliL2Cache *cache,
+ target_phys_addr_t offset, uint32_t val)
+{
+ DPRINT_L2("MALI L2 CACHE: write offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+
+ switch (offset) {
+ case MALI_L2CACHE_REG_COMMAND:
+ cache->command = val;
+ break;
+ case MALI_L2CACHE_REG_ENABLE:
+ cache->enable = val;
+ break;
+ case MALI_L2CACHE_REG_STATUS:
+ DPRINT_L1("MALI L2 CACHE: writing to RO register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ default:
+ DPRINT_L1("MALI L2 CACHE: writing non-existing register! Offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ }
+}
+
+static void exynos4210_g3d_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210G3DState *s = (Exynos4210G3DState *)opaque;
+
+ switch (offset) {
+ case GP_OFF_START ... GP_OFF_END:
+ exynos4210_maligp_write(&s->gp, offset - GP_OFF_START, val);
+ break;
+ case PP0_OFF_START ... PP0_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#0: ");
+ exynos4210_malipp_write(&s->pp[0], offset - PP0_OFF_START, val);
+ break;
+ case PP1_OFF_START ... PP1_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#1: ");
+ exynos4210_malipp_write(&s->pp[1], offset - PP1_OFF_START, val);
+ break;
+ case PP2_OFF_START ... PP2_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#2: ");
+ exynos4210_malipp_write(&s->pp[2], offset - PP2_OFF_START, val);
+ break;
+ case PP3_OFF_START ... PP3_OFF_END:
+ DPRINT_L2("MALI PIXEL PROCESSOR#3: ");
+ exynos4210_malipp_write(&s->pp[3], offset - PP3_OFF_START, val);
+ break;
+ case GP_MMU_OFF_START ... GP_MMU_OFF_END:
+ DPRINT_L2("MALI GEOM PROC MMU: ");
+ exynos4210_malimmu_write(&s->gp_mmu, offset - GP_MMU_OFF_START, val);
+ break;
+ case PP0_MMU_OFF_START ... PP0_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#0 MMU: ");
+ exynos4210_malimmu_write(&s->pp_mmu[0],
+ offset - PP0_MMU_OFF_START, val);
+ break;
+ case PP1_MMU_OFF_START ... PP1_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#1 MMU: ");
+ exynos4210_malimmu_write(&s->pp_mmu[1],
+ offset - PP1_MMU_OFF_START, val);
+ break;
+ case PP2_MMU_OFF_START ... PP2_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#2 MMU: ");
+ exynos4210_malimmu_write(&s->pp_mmu[2],
+ offset - PP2_MMU_OFF_START, val);
+ break;
+ case PP3_MMU_OFF_START ... PP3_MMU_OFF_END:
+ DPRINT_L2("MALI PIX PROC#3 MMU: ");
+ exynos4210_malimmu_write(&s->pp_mmu[3],
+ offset - PP3_MMU_OFF_START, val);
+ break;
+ case L2_OFF_START ... L2_OFF_END:
+ exynos4210_mali_l2cache_write(&s->l2_cache, offset - L2_OFF_START, val);
+ break;
+ default:
+ DPRINT_L1("MALI UNKNOWN REGION: write offset 0x"
+ TARGET_FMT_plx ", value=%d(0x%x) \n", offset, val, val);
+ break;
+ }
+}
+
+static const MemoryRegionOps exynos4210_g3d_mmio_ops = {
+ .read = exynos4210_g3d_read,
+ .write = exynos4210_g3d_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int exynos4210_g3d_init(SysBusDevice *dev)
+{
+ Exynos4210G3DState *s = FROM_SYSBUS(Exynos4210G3DState, dev);
+
+ sysbus_init_irq(dev, &s->pp[0].irq_pp);
+ sysbus_init_irq(dev, &s->pp[1].irq_pp);
+ sysbus_init_irq(dev, &s->pp[2].irq_pp);
+ sysbus_init_irq(dev, &s->pp[3].irq_pp);
+ sysbus_init_irq(dev, &s->gp.irq_gp);
+ sysbus_init_irq(dev, &s->irq_pmu);
+ sysbus_init_irq(dev, &s->pp_mmu[0].irq_mmu);
+ sysbus_init_irq(dev, &s->pp_mmu[1].irq_mmu);
+ sysbus_init_irq(dev, &s->pp_mmu[2].irq_mmu);
+ sysbus_init_irq(dev, &s->pp_mmu[3].irq_mmu);
+ sysbus_init_irq(dev, &s->gp_mmu.irq_mmu);
+
+ memory_region_init_io(&s->iomem, &exynos4210_g3d_mmio_ops, s,
+ "exynos4210.g3d", EXYNOS4210_G3D_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_g3d_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ dc->reset = exynos4210_g3d_reset;
+ k->init = exynos4210_g3d_init;
+}
+
+static TypeInfo exynos4210_g3d_info = {
+ .name = "exynos4210.g3d",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210G3DState),
+ .class_init = exynos4210_g3d_class_init,
+};
+
+static void exynos4210_g3d_register_devices(void)
+{
+ type_register_static(&exynos4210_g3d_info);
+}
+
+type_init(exynos4210_g3d_register_devices)
EXT_GIC_ID_EXTINT12,
EXT_GIC_ID_EXTINT13,
EXT_GIC_ID_EXTINT14,
- EXT_GIC_ID_EXTINT15
+ EXT_GIC_ID_EXTINT15,
+ EXT_GIC_ID_EXTINT16_31,
};
/*
{ EXT_GIC_ID_MIXER },
/* int combiner group 37 */
{ EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
- EXT_GIC_ID_EXTINT7 },
- /* groups 38-50 */
- { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
+ EXT_GIC_ID_EXTINT7 },
+ /* int combiner group 38 */
+ { EXT_GIC_ID_EXTINT8, EXT_GIC_ID_EXTINT9, EXT_GIC_ID_EXTINT10,
+ EXT_GIC_ID_EXTINT11, EXT_GIC_ID_EXTINT12, EXT_GIC_ID_EXTINT13,
+ EXT_GIC_ID_EXTINT14, EXT_GIC_ID_EXTINT15 },
+ /* int combiner groups 39-43 */
+ { EXT_GIC_ID_EXTINT16_31 },
+ { EXT_GIC_ID_EXTINT0 },
+ { EXT_GIC_ID_EXTINT1 },
+ { EXT_GIC_ID_EXTINT2 },
+ { EXT_GIC_ID_EXTINT3 },
+ /* groups 44-50 */
+ { }, { }, { }, { }, { }, { }, { },
/* int combiner group 51 */
{ EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* group 52 */
typedef struct {
SysBusDevice busdev;
- qemu_irq pic_irq[EXYNOS4210_NCPUS]; /* output IRQs to PICs */
- uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */
+ qemu_irq out[QDEV_MAX_IRQ]; /* output IRQs */
+ uint32_t n_out; /* outputs amount */
+ uint32_t gpio_level[QDEV_MAX_IRQ]; /* Input levels */
+ uint32_t n_in; /* inputs amount */
+ uint32_t group_size; /* input group size */
} Exynos4210IRQGateState;
+static Property exynos4210_irq_gate_properties[] = {
+ DEFINE_PROP_UINT32("n_out", Exynos4210IRQGateState, n_out, 1),
+ DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static const VMStateDescription vmstate_exynos4210_irq_gate = {
.name = "exynos4210.irq_gate",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState,
- EXYNOS4210_IRQ_GATE_NINPUTS),
+ VMSTATE_UINT32(n_out, Exynos4210IRQGateState),
+ VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState, QDEV_MAX_IRQ),
+ VMSTATE_UINT32(n_in, Exynos4210IRQGateState),
+ VMSTATE_UINT32(group_size, Exynos4210IRQGateState),
VMSTATE_END_OF_LIST()
}
};
{
Exynos4210IRQGateState *s =
(Exynos4210IRQGateState *)opaque;
- uint32_t odd, even;
-
- if (irq & 1) {
- odd = irq;
- even = irq & ~1;
- } else {
- even = irq;
- odd = irq | 1;
- }
+ uint32_t n_out, n_group, i;
+
+ assert(irq < s->n_in);
+
+ n_out = irq / s->group_size;
+ n_group = n_out * s->group_size;
- assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS);
s->gpio_level[irq] = level;
- if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) {
- qemu_irq_raise(s->pic_irq[even >> 1]);
- } else {
- qemu_irq_lower(s->pic_irq[even >> 1]);
+ for (i = 0; i < s->group_size; i++) {
+ if (s->gpio_level[n_group + i] >= 1) {
+ qemu_irq_raise(s->out[n_out]);
+ return;
+ }
}
+ qemu_irq_lower(s->out[n_out]);
+
return;
}
Exynos4210IRQGateState *s =
FROM_SYSBUS(Exynos4210IRQGateState, dev);
+ /* Gate will make each input group of size n_in / n_out */
+ assert((s->n_in % s->n_out) == 0);
+
+ s->group_size = s->n_in / s->n_out;
+
/* Allocate general purpose input signals and connect a handler to each of
* them */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler,
- EXYNOS4210_IRQ_GATE_NINPUTS);
+ s->n_in);
- /* Connect SysBusDev irqs to device specific irqs */
- for (i = 0; i < EXYNOS4210_NCPUS; i++) {
- sysbus_init_irq(dev, &s->pic_irq[i]);
+ /* Pass gate outs to SysBusDev */
+ for (i = 0; i < s->n_out; i++) {
+ sysbus_init_irq(dev, &s->out[i]);
}
return 0;
k->init = exynos4210_irq_gate_init;
dc->reset = exynos4210_irq_gate_reset;
dc->vmsd = &vmstate_exynos4210_irq_gate;
+ dc->props = exynos4210_irq_gate_properties;
}
static TypeInfo exynos4210_irq_gate_info = {
--- /dev/null
+/*
+ * Exynos4210 I2C Bus Serial Interface Emulation
+ *
+ * Copyright (C) 2012 Samsung Electronics Co Ltd.
+ * Maksim Kozlov, <m.kozlov@samsung.com>
+ * Igor Mitsyanko, <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "i2c.h"
+
+#ifndef EXYNOS4_I2C_DEBUG
+#define EXYNOS4_I2C_DEBUG 0
+#endif
+
+#define TYPE_EXYNOS4_I2C "exynos4210.i2c"
+#define EXYNOS4_I2C(obj) \
+ OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C)
+
+/* Exynos4210 I2C memory map */
+#define EXYNOS4_I2C_MEM_SIZE 0x14
+#define I2CCON_ADDR 0x00 /* control register */
+#define I2CSTAT_ADDR 0x04 /* control/status register */
+#define I2CADD_ADDR 0x08 /* address register */
+#define I2CDS_ADDR 0x0c /* data shift register */
+#define I2CLC_ADDR 0x10 /* line control register */
+
+#define I2CCON_ACK_GEN (1 << 7)
+#define I2CCON_INTRS_EN (1 << 5)
+#define I2CCON_INT_PEND (1 << 4)
+
+#define EXYNOS4_I2C_MODE(reg) (((reg) >> 6) & 3)
+#define I2C_IN_MASTER_MODE(reg) (((reg) >> 6) & 2)
+#define I2CMODE_MASTER_Rx 0x2
+#define I2CMODE_MASTER_Tx 0x3
+#define I2CSTAT_LAST_BIT (1 << 0)
+#define I2CSTAT_OUTPUT_EN (1 << 4)
+#define I2CSTAT_START_BUSY (1 << 5)
+
+
+#if EXYNOS4_I2C_DEBUG
+#define DPRINT(fmt, args...) \
+ do { fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0)
+
+static const char *exynos4_i2c_get_regname(unsigned offset)
+{
+ switch (offset) {
+ case I2CCON_ADDR:
+ return "I2CCON";
+ case I2CSTAT_ADDR:
+ return "I2CSTAT";
+ case I2CADD_ADDR:
+ return "I2CADD";
+ case I2CDS_ADDR:
+ return "I2CDS";
+ case I2CLC_ADDR:
+ return "I2CLC";
+ default:
+ return "[?]";
+ }
+}
+
+#else
+#define DPRINT(fmt, args...) do { } while (0)
+#endif
+
+typedef struct Exynos4210I2CState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ i2c_bus *bus;
+ qemu_irq irq;
+
+ uint8_t i2ccon;
+ uint8_t i2cstat;
+ uint8_t i2cadd;
+ uint8_t i2cds;
+ uint8_t i2clc;
+ bool scl_free;
+} Exynos4210I2CState;
+
+static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
+{
+ if (s->i2ccon & I2CCON_INTRS_EN) {
+ s->i2ccon |= I2CCON_INT_PEND;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static void exynos4210_i2c_data_receive(void *opaque)
+{
+ Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
+ int ret;
+
+ s->i2cstat &= ~I2CSTAT_LAST_BIT;
+ s->scl_free = false;
+ ret = i2c_recv(s->bus);
+ if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
+ s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */
+ } else {
+ s->i2cds = ret;
+ }
+ exynos4210_i2c_raise_interrupt(s);
+}
+
+static void exynos4210_i2c_data_send(void *opaque)
+{
+ Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
+
+ s->i2cstat &= ~I2CSTAT_LAST_BIT;
+ s->scl_free = false;
+ if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
+ s->i2cstat |= I2CSTAT_LAST_BIT;
+ }
+ exynos4210_i2c_raise_interrupt(s);
+}
+
+static uint64_t exynos4210_i2c_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
+ uint8_t value;
+
+ switch (offset) {
+ case I2CCON_ADDR:
+ value = s->i2ccon;
+ break;
+ case I2CSTAT_ADDR:
+ value = s->i2cstat;
+ break;
+ case I2CADD_ADDR:
+ value = s->i2cadd;
+ break;
+ case I2CDS_ADDR:
+ value = s->i2cds;
+ s->scl_free = true;
+ if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx &&
+ (s->i2cstat & I2CSTAT_START_BUSY) &&
+ !(s->i2ccon & I2CCON_INT_PEND)) {
+ exynos4210_i2c_data_receive(s);
+ }
+ break;
+ case I2CLC_ADDR:
+ value = s->i2clc;
+ break;
+ default:
+ value = 0;
+ DPRINT("ERROR: Bad read offset 0x%x\n", (unsigned int)offset);
+ break;
+ }
+
+ DPRINT("read %s [0x%02x] -> 0x%02x\n", exynos4_i2c_get_regname(offset),
+ (unsigned int)offset, value);
+ return value;
+}
+
+static void exynos4210_i2c_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
+ uint8_t v = value & 0xff;
+
+ DPRINT("write %s [0x%02x] <- 0x%02x\n", exynos4_i2c_get_regname(offset),
+ (unsigned int)offset, v);
+
+ switch (offset) {
+ case I2CCON_ADDR:
+ s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND);
+ if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) {
+ s->i2ccon &= ~I2CCON_INT_PEND;
+ qemu_irq_lower(s->irq);
+ if (!(s->i2ccon & I2CCON_INTRS_EN)) {
+ s->i2cstat &= ~I2CSTAT_START_BUSY;
+ }
+
+ if (s->i2cstat & I2CSTAT_START_BUSY) {
+ if (s->scl_free) {
+ if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) {
+ exynos4210_i2c_data_send(s);
+ } else if (EXYNOS4_I2C_MODE(s->i2cstat) ==
+ I2CMODE_MASTER_Rx) {
+ exynos4210_i2c_data_receive(s);
+ }
+ } else {
+ s->i2ccon |= I2CCON_INT_PEND;
+ qemu_irq_raise(s->irq);
+ }
+ }
+ }
+ break;
+ case I2CSTAT_ADDR:
+ s->i2cstat =
+ (s->i2cstat & I2CSTAT_START_BUSY) | (v & ~I2CSTAT_START_BUSY);
+
+ if (!(s->i2cstat & I2CSTAT_OUTPUT_EN)) {
+ s->i2cstat &= ~I2CSTAT_START_BUSY;
+ s->scl_free = true;
+ qemu_irq_lower(s->irq);
+ break;
+ }
+
+ /* Nothing to do if in i2c slave mode */
+ if (!I2C_IN_MASTER_MODE(s->i2cstat)) {
+ break;
+ }
+
+ if (v & I2CSTAT_START_BUSY) {
+ s->i2cstat &= ~I2CSTAT_LAST_BIT;
+ s->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */
+ s->scl_free = false;
+
+ /* Generate start bit and send slave address */
+ if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) &&
+ (s->i2ccon & I2CCON_ACK_GEN)) {
+ s->i2cstat |= I2CSTAT_LAST_BIT;
+ } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) {
+ exynos4210_i2c_data_receive(s);
+ }
+ exynos4210_i2c_raise_interrupt(s);
+ } else {
+ i2c_end_transfer(s->bus);
+ if (!(s->i2ccon & I2CCON_INT_PEND)) {
+ s->i2cstat &= ~I2CSTAT_START_BUSY;
+ }
+ s->scl_free = true;
+ }
+ break;
+ case I2CADD_ADDR:
+ if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) {
+ s->i2cadd = v;
+ }
+ break;
+ case I2CDS_ADDR:
+ if (s->i2cstat & I2CSTAT_OUTPUT_EN) {
+ s->i2cds = v;
+ s->scl_free = true;
+ if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx &&
+ (s->i2cstat & I2CSTAT_START_BUSY) &&
+ !(s->i2ccon & I2CCON_INT_PEND)) {
+ exynos4210_i2c_data_send(s);
+ }
+ }
+ break;
+ case I2CLC_ADDR:
+ s->i2clc = v;
+ break;
+ default:
+ DPRINT("ERROR: Bad write offset 0x%x\n", (unsigned int)offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps exynos4210_i2c_ops = {
+ .read = exynos4210_i2c_read,
+ .write = exynos4210_i2c_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription exynos4210_i2c_vmstate = {
+ .name = TYPE_EXYNOS4_I2C,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(i2ccon, Exynos4210I2CState),
+ VMSTATE_UINT8(i2cstat, Exynos4210I2CState),
+ VMSTATE_UINT8(i2cds, Exynos4210I2CState),
+ VMSTATE_UINT8(i2cadd, Exynos4210I2CState),
+ VMSTATE_UINT8(i2clc, Exynos4210I2CState),
+ VMSTATE_BOOL(scl_free, Exynos4210I2CState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void exynos4210_i2c_reset(DeviceState *d)
+{
+ Exynos4210I2CState *s = EXYNOS4_I2C(d);
+
+ s->i2ccon = 0x00;
+ s->i2cstat = 0x00;
+ s->i2cds = 0xFF;
+ s->i2clc = 0x00;
+ s->i2cadd = 0xFF;
+ s->scl_free = true;
+}
+
+static int exynos4210_i2c_realize(SysBusDevice *dev)
+{
+ Exynos4210I2CState *s = EXYNOS4_I2C(dev);
+
+ memory_region_init_io(&s->iomem, &exynos4210_i2c_ops, s, TYPE_EXYNOS4_I2C,
+ EXYNOS4_I2C_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+ s->bus = i2c_init_bus(&dev->qdev, "i2c");
+ return 0;
+}
+
+static void exynos4210_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ dc->vmsd = &exynos4210_i2c_vmstate;
+ dc->reset = exynos4210_i2c_reset;
+ sbdc->init = exynos4210_i2c_realize;
+}
+
+static const TypeInfo exynos4210_i2c_info = {
+ .name = TYPE_EXYNOS4_I2C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210I2CState),
+ .class_init = exynos4210_i2c_class_init,
+};
+
+static void exynos4210_i2c_register_types(void)
+{
+ type_register_static(&exynos4210_i2c_info);
+}
+
+type_init(exynos4210_i2c_register_types)
--- /dev/null
+/*
+ * Samsung exynos4210 I2S driver
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Vorobiov Stanislav <s.vorobiov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "exynos4210_i2s.h"
+#include "exec-memory.h"
+
+/* #define DEBUG_I2S */
+
+#ifdef DEBUG_I2S
+#define DPRINTF(fmt, ...) \
+ do { \
+ fprintf(stdout, "I2S: [%s:%d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); \
+ } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+/* Registers */
+#define IISCON 0x00
+#define IISMOD 0x04
+#define IISFIC 0x08
+#define IISPSR 0x0C
+#define IISTXD 0x10
+#define IISRXD 0x14
+#define IISFICS 0x18
+#define IISTXDS 0x1C
+#define IISAHB 0x20
+#define IISSTR0 0x24
+#define IISSIZE 0x28
+#define IISTRNCNT 0x2C
+#define IISLVL0ADDR 0x30
+#define IISLVL1ADDR 0x34
+#define IISLVL2ADDR 0x38
+#define IISLVL3ADDR 0x3C
+#define IISSTR1 0x40
+
+#define I2S_REGS_MEM_SIZE 0x44
+#define I2S_REGS_NUM (I2S_REGS_MEM_SIZE >> 2)
+
+#define I_(reg) (reg / sizeof(uint32_t))
+
+/* Interface Control Register */
+#define IISCON_I2SACTIVE (1 << 0)
+#define IISCON_RXDMACTIVE (1 << 1)
+#define IISCON_TXDMACTIVE (1 << 2)
+#define IISCON_RXCHPAUSE (1 << 3)
+#define IISCON_TXCHPAUSE (1 << 4)
+#define IISCON_RXDMAPAUSE (1 << 5)
+#define IISCON_TXDMAPAUSE (1 << 6)
+#define IISCON_FRXFULL (1 << 7)
+#define IISCON_FTX0FULL (1 << 8)
+#define IISCON_FRXEMPT (1 << 9)
+#define IISCON_FTX0EMPT (1 << 10)
+#define IISCON_LRI (1 << 11)
+#define IISCON_FTX1FULL (1 << 12)
+#define IISCON_FTX2FULL (1 << 13)
+#define IISCON_FTX1EMPT (1 << 14)
+#define IISCON_FTX2EMPT (1 << 15)
+#define IISCON_FTXURINTEN (1 << 16)
+#define IISCON_FTXURSTATUS (1 << 17)
+#define IISCON_TXSDMACTIVE (1 << 18)
+#define IISCON_TXSDMAPAUSE (1 << 20)
+#define IISCON_FTXSFULL (1 << 21)
+#define IISCON_FTXSEMPT (1 << 22)
+#define IISCON_FTXSURINTEN (1 << 23)
+#define IISCON_FTXSURSTATUS (1 << 24)
+#define IISCON_FRXOFINTEN (1 << 25)
+#define IISCON_FRXOFSTATUS (1 << 26)
+#define IISCON_SW_RST (1 << 31)
+
+#define IISCON_WRITE_MASK (0x8295007F)
+
+/* AHB DMA Control Register */
+#define IISAHB_WRITE_MASK 0xFF0000FB
+#define IISAHB_LVL3EN (1 << 27)
+#define IISAHB_LVL2EN (1 << 26)
+#define IISAHB_LVL1EN (1 << 25)
+#define IISAHB_LVL0EN (1 << 24)
+#define IISAHB_LVL3INT (1 << 23)
+#define IISAHB_LVL2INT (1 << 22)
+#define IISAHB_LVL1INT (1 << 21)
+#define IISAHB_LVL0INT (1 << 20)
+#define IISAHB_LVL3CLR (1 << 19)
+#define IISAHB_LVL2CLR (1 << 18)
+#define IISAHB_LVL1CLR (1 << 17)
+#define IISAHB_LVL0CLR (1 << 16)
+#define IISAHB_DMA_STRADDRRST (1 << 7)
+#define IISAHB_DMA_STRADDRTOG (1 << 6)
+#define IISAHB_DMARLD (1 << 5)
+#define IISAHB_INTMASK (1 << 3)
+#define IISAHB_DMAINT (1 << 2)
+#define IISAHB_DMACLR (1 << 1)
+#define IISAHB_DMAEN (1 << 0)
+
+/* AHB DMA Size Register */
+#define IISSIZE_TRNS_SIZE_OFFSET 16
+#define IISSIZE_TRNS_SIZE_MASK 0xFFFF
+
+/* AHB DMA Transfer Count Register */
+#define IISTRNCNT_OFFSET 0
+#define IISTRNCNT_MASK 0xFFFFFF
+
+typedef struct {
+ const char *name;
+ target_phys_addr_t offset;
+ uint32_t reset_value;
+} Exynos4210I2SReg;
+
+static Exynos4210I2SReg exynos4210_i2s_regs[I2S_REGS_NUM] = {
+ {"IISCON", IISCON, 0x00000000},
+ {"IISMOD", IISMOD, 0x00000000},
+ {"IISFIC", IISFIC, 0x00000000},
+ {"IISPSR", IISPSR, 0x00000000},
+ {"IISTXD", IISTXD, 0x00000000},
+ {"IISRXD", IISRXD, 0x00000000},
+ {"IISFICS", IISFICS, 0x00000000},
+ {"IISTXDS", IISTXDS, 0x00000000},
+ {"IISAHB", IISAHB, 0x00000000},
+ {"IISSTR0", IISSTR0, 0x00000000},
+ {"IISSIZE", IISSIZE, 0x7FFF0000},
+ {"IISTRNCNT", IISTRNCNT, 0x00000000},
+ {"IISLVL0ADDR", IISLVL0ADDR, 0x00000000},
+ {"IISLVL1ADDR", IISLVL1ADDR, 0x00000000},
+ {"IISLVL2ADDR", IISLVL2ADDR, 0x00000000},
+ {"IISLVL3ADDR", IISLVL3ADDR, 0x00000000},
+ {"IISSTR1", IISSTR1, 0x00000000},
+};
+
+static struct {
+ uint32_t ahb_en_bit;
+ uint32_t ahb_int_bit;
+ uint32_t ahb_clr_bit;
+ int reg_offset;
+} lvl_regs[] = {
+ { IISAHB_LVL3EN, IISAHB_LVL3INT, IISAHB_LVL3CLR, IISLVL3ADDR },
+ { IISAHB_LVL2EN, IISAHB_LVL2INT, IISAHB_LVL2CLR, IISLVL2ADDR },
+ { IISAHB_LVL1EN, IISAHB_LVL1INT, IISAHB_LVL1CLR, IISLVL1ADDR },
+ { IISAHB_LVL0EN, IISAHB_LVL0INT, IISAHB_LVL0CLR, IISLVL0ADDR },
+};
+
+typedef struct {
+ BusState qbus;
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ uint32_t reg[I2S_REGS_NUM];
+} Exynos4210I2SBus;
+
+static Exynos4210I2SSlave *get_slave(Exynos4210I2SBus *bus)
+{
+ DeviceState *dev = QTAILQ_FIRST(&bus->qbus.children);
+
+ if (dev) {
+ return EXYNOS4210_I2S_SLAVE_FROM_QDEV(dev);
+ }
+
+ return NULL;
+}
+
+static void reset_internal(BusState *qbus, bool reset_slave)
+{
+ Exynos4210I2SBus *bus = DO_UPCAST(Exynos4210I2SBus, qbus, qbus);
+ Exynos4210I2SSlave *s;
+ int i;
+
+ DPRINTF("enter\n");
+
+ qemu_irq_lower(bus->irq);
+
+ for (i = 0; i < ARRAY_SIZE(exynos4210_i2s_regs); i++) {
+ bus->reg[i] = exynos4210_i2s_regs[i].reset_value;
+ }
+
+ if (reset_slave) {
+ s = get_slave(bus);
+
+ if (s != NULL) {
+ device_reset(&s->qdev);
+ }
+ }
+}
+
+static uint32_t get_dma_size_words(Exynos4210I2SBus *bus)
+{
+ return (bus->reg[I_(IISSIZE)] >> IISSIZE_TRNS_SIZE_OFFSET) &
+ IISSIZE_TRNS_SIZE_MASK;
+}
+
+static uint32_t get_dma_trncnt_words(Exynos4210I2SBus *bus)
+{
+ return (bus->reg[I_(IISTRNCNT)] >> IISTRNCNT_OFFSET) &
+ IISTRNCNT_MASK;
+}
+
+static void set_dma_trncnt_words(Exynos4210I2SBus *bus, uint32_t trncnt_words)
+{
+ bus->reg[I_(IISTRNCNT)] &= ~(IISTRNCNT_MASK << IISTRNCNT_OFFSET);
+ bus->reg[I_(IISTRNCNT)] |=
+ ((trncnt_words & IISTRNCNT_MASK) << IISTRNCNT_OFFSET);
+}
+
+static int exynos4210_i2s_bus_reset(BusState *qbus);
+
+static struct BusInfo exynos4210_i2s_bus_info = {
+ .name = "Exynos4210.I2S",
+ .size = sizeof(Exynos4210I2SBus),
+ .reset = exynos4210_i2s_bus_reset
+};
+
+static const VMStateDescription vmstate_exynos4210_i2s_bus = {
+ .name = "exynos4210.i2s_bus",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, Exynos4210I2SBus,
+ I2S_REGS_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#ifdef DEBUG_I2S
+static const char *exynos4210_i2s_regname(Exynos4210I2SBus *bus,
+ target_phys_addr_t offset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(exynos4210_i2s_regs); i++) {
+ if (offset == exynos4210_i2s_regs[i].offset) {
+ return exynos4210_i2s_regs[i].name;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+static void exynos4210_i2s_bus_write(void *opaque,
+ target_phys_addr_t offset,
+ uint64_t value,
+ unsigned size)
+{
+ Exynos4210I2SBus *bus = opaque;
+ Exynos4210I2SSlave *s;
+ Exynos4210I2SSlaveClass *sc;
+ int i;
+
+ s = get_slave(bus);
+
+ assert(s != NULL);
+
+ if (s == NULL) {
+ hw_error("Exynos I2S cannot operate without a slave\n");
+ }
+
+ sc = EXYNOS4210_I2S_SLAVE_GET_CLASS(s);
+
+ switch (offset) {
+ case IISCON:
+ if (!(value & IISCON_SW_RST) &&
+ (bus->reg[I_(IISCON)] & IISCON_SW_RST)) {
+ reset_internal(&bus->qbus, 1);
+ }
+
+ bus->reg[I_(offset)] = (bus->reg[I_(offset)] & ~IISCON_WRITE_MASK) |
+ (value & IISCON_WRITE_MASK);
+
+ DPRINTF("0x%.8X -> %s\n",
+ bus->reg[I_(offset)],
+ exynos4210_i2s_regname(bus, offset));
+
+ break;
+ case IISAHB:
+ if (((value & IISAHB_DMAEN) != 0) && ((value & IISAHB_DMARLD) == 0)) {
+ hw_error("Non auto-reload DMA is not supported\n");
+ }
+
+ if (((value & IISAHB_DMAEN) != 0) && ((value & IISAHB_INTMASK) == 0)) {
+ hw_error("Non buffer level DMA interrupt is not supported\n");
+ }
+
+ if (((value & IISAHB_DMAEN) != 0) &&
+ ((value & IISAHB_DMA_STRADDRTOG) != 0)) {
+ hw_error("DMA start address toggle is not supported\n");
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lvl_regs); ++i) {
+ if ((value & lvl_regs[i].ahb_clr_bit) &&
+ (bus->reg[I_(IISAHB)] & lvl_regs[i].ahb_int_bit)) {
+ qemu_irq_lower(bus->irq);
+ bus->reg[I_(IISAHB)] &= ~lvl_regs[i].ahb_int_bit;
+ }
+ }
+
+ if ((value & IISAHB_DMACLR) &&
+ (bus->reg[I_(IISAHB)] & IISAHB_DMAINT)) {
+ qemu_irq_lower(bus->irq);
+ bus->reg[I_(IISAHB)] &= ~IISAHB_DMAINT;
+ }
+
+ if ((value & IISAHB_DMAEN) !=
+ (bus->reg[I_(IISAHB)] & IISAHB_DMAEN)) {
+ if ((value & IISAHB_DMAEN) == 0) {
+ qemu_irq_lower(bus->irq);
+ }
+ if (sc->dma_enable) {
+ sc->dma_enable(s, ((value & IISAHB_DMAEN) != 0));
+ }
+ }
+
+ bus->reg[I_(offset)] = (bus->reg[I_(offset)] & ~IISAHB_WRITE_MASK) |
+ (value & IISAHB_WRITE_MASK);
+
+ DPRINTF("0x%.8X -> %s\n",
+ bus->reg[I_(offset)],
+ exynos4210_i2s_regname(bus, offset));
+
+ break;
+ case IISTRNCNT:
+ hw_error("Cannot write IISTRNCNT\n");
+ break;
+ case IISSIZE:
+ if (value == 0) {
+ hw_error("IISSIZE cannot be 0\n");
+ }
+
+ bus->reg[I_(offset)] = value;
+
+ if (get_dma_size_words(bus) <= get_dma_trncnt_words(bus)) {
+ set_dma_trncnt_words(bus, 0);
+ }
+
+ DPRINTF("0x%.8X -> %s\n",
+ bus->reg[I_(offset)],
+ exynos4210_i2s_regname(bus, offset));
+
+ break;
+ case IISLVL0ADDR:
+ case IISLVL1ADDR:
+ case IISLVL2ADDR:
+ case IISLVL3ADDR:
+ case IISTXD:
+ case IISMOD:
+ case IISFIC:
+ case IISPSR:
+ case IISTXDS:
+ case IISFICS:
+ case IISSTR0:
+ case IISSTR1:
+ bus->reg[I_(offset)] = value;
+
+ DPRINTF("0x%.8X -> %s\n",
+ bus->reg[I_(offset)],
+ exynos4210_i2s_regname(bus, offset));
+
+ break;
+ default:
+ hw_error("Bad offset: 0x%X\n", (int)offset);
+ }
+}
+
+static uint64_t exynos4210_i2s_bus_read(void *opaque,
+ target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210I2SBus *bus = opaque;
+
+ if (offset > (I2S_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ hw_error("Bad offset: 0x%X\n", (int)offset);
+ return 0;
+ }
+
+ DPRINTF("%s -> 0x%.8X\n",
+ exynos4210_i2s_regname(bus, offset),
+ bus->reg[I_(offset)]);
+
+ return bus->reg[I_(offset)];
+}
+
+static int exynos4210_i2s_bus_reset(BusState *qbus)
+{
+ reset_internal(qbus, 0);
+
+ return 0;
+}
+
+static const MemoryRegionOps exynos4210_i2s_bus_ops = {
+ .read = exynos4210_i2s_bus_read,
+ .write = exynos4210_i2s_bus_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .max_access_size = 4,
+ .unaligned = false
+ },
+};
+
+BusState *exynos4210_i2s_bus_new(const char *name,
+ target_phys_addr_t addr,
+ qemu_irq irq)
+{
+ Exynos4210I2SBus *bus;
+
+ bus = FROM_QBUS(Exynos4210I2SBus,
+ qbus_create(&exynos4210_i2s_bus_info, NULL, name));
+ vmstate_register(NULL, -1, &vmstate_exynos4210_i2s_bus, bus);
+
+ memory_region_init_io(&bus->iomem,
+ &exynos4210_i2s_bus_ops,
+ bus,
+ "exynos4210.i2s",
+ I2S_REGS_MEM_SIZE);
+
+ memory_region_add_subregion(get_system_memory(),
+ addr,
+ &bus->iomem);
+
+ bus->irq = irq;
+
+ return &bus->qbus;
+}
+
+bool exynos4210_i2s_dma_enabled(BusState *qbus)
+{
+ Exynos4210I2SBus *bus = DO_UPCAST(Exynos4210I2SBus, qbus, qbus);
+
+ return ((bus->reg[I_(IISAHB)] & IISAHB_DMAEN) != 0);
+}
+
+uint32_t exynos4210_i2s_dma_get_words_available(BusState *qbus)
+{
+ Exynos4210I2SBus *bus = DO_UPCAST(Exynos4210I2SBus, qbus, qbus);
+
+ return get_dma_size_words(bus);
+}
+
+void exynos4210_i2s_dma_read(BusState *qbus, void *buf, uint32_t num_words)
+{
+ Exynos4210I2SBus *bus = DO_UPCAST(Exynos4210I2SBus, qbus, qbus);
+ target_phys_addr_t addr;
+ uint32_t size_words, trncnt_words;
+
+ assert(num_words <= exynos4210_i2s_dma_get_words_available(qbus));
+
+ if (num_words > exynos4210_i2s_dma_get_words_available(qbus)) {
+ hw_error("Bad DMA read length: %d\n", num_words);
+ }
+
+ size_words = get_dma_size_words(bus);
+ addr = bus->reg[I_(IISSTR0)];
+ trncnt_words = get_dma_trncnt_words(bus);
+
+ assert(trncnt_words < size_words);
+
+ if (num_words > (size_words - trncnt_words)) {
+ cpu_physical_memory_read(addr +
+ (trncnt_words * EXYNOS4210_I2S_WORD_LEN),
+ buf,
+ (size_words - trncnt_words) * EXYNOS4210_I2S_WORD_LEN);
+ num_words -= (size_words - trncnt_words);
+ buf += (size_words - trncnt_words) * EXYNOS4210_I2S_WORD_LEN;
+ trncnt_words = 0;
+ }
+
+ cpu_physical_memory_read(addr + (trncnt_words * EXYNOS4210_I2S_WORD_LEN),
+ buf,
+ num_words * EXYNOS4210_I2S_WORD_LEN);
+}
+
+void exynos4210_i2s_dma_advance(BusState *qbus, uint32_t num_words)
+{
+ Exynos4210I2SBus *bus = DO_UPCAST(Exynos4210I2SBus, qbus, qbus);
+ uint32_t size_words, trncnt_words;
+ int i;
+
+ assert(num_words <= exynos4210_i2s_dma_get_words_available(qbus));
+
+ if (num_words > exynos4210_i2s_dma_get_words_available(qbus)) {
+ hw_error("Bad DMA read length: %d\n", num_words);
+ }
+
+ size_words = get_dma_size_words(bus);
+ trncnt_words = get_dma_trncnt_words(bus);
+
+ for (i = 0; i < ARRAY_SIZE(lvl_regs); ++i) {
+ target_phys_addr_t dma_offset;
+ uint32_t tmp_num_words = num_words, tmp_trncnt_words = trncnt_words;
+
+ if ((bus->reg[I_(IISAHB)] & lvl_regs[i].ahb_en_bit) == 0) {
+ continue;
+ }
+
+ if (bus->reg[I_(lvl_regs[i].reg_offset)] < bus->reg[I_(IISSTR0)]) {
+ continue;
+ }
+
+ dma_offset = bus->reg[I_(lvl_regs[i].reg_offset)] -
+ bus->reg[I_(IISSTR0)];
+
+ if (tmp_num_words > (size_words - tmp_trncnt_words)) {
+ if ((dma_offset >= (tmp_trncnt_words * EXYNOS4210_I2S_WORD_LEN)) &&
+ (dma_offset < size_words * EXYNOS4210_I2S_WORD_LEN)) {
+ bus->reg[I_(IISAHB)] |= lvl_regs[i].ahb_int_bit;
+ qemu_irq_raise(bus->irq);
+ break;
+ }
+
+ tmp_num_words -= (size_words - tmp_trncnt_words);
+ tmp_trncnt_words = 0;
+ }
+
+ if ((dma_offset >= (tmp_trncnt_words * EXYNOS4210_I2S_WORD_LEN)) &&
+ (dma_offset <
+ (tmp_trncnt_words + tmp_num_words) * EXYNOS4210_I2S_WORD_LEN)) {
+ bus->reg[I_(IISAHB)] |= lvl_regs[i].ahb_int_bit;
+ qemu_irq_raise(bus->irq);
+ }
+ }
+
+ set_dma_trncnt_words(bus, (trncnt_words + num_words) % size_words);
+}
+
+const VMStateDescription vmstate_exynos4210_i2s_slave = {
+ .name = "Exynos4210I2SSlave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1
+};
+
+static int exynos4210_i2s_slave_qdev_init(DeviceState *dev)
+{
+ Exynos4210I2SSlave *s = EXYNOS4210_I2S_SLAVE_FROM_QDEV(dev);
+ Exynos4210I2SSlaveClass *sc = EXYNOS4210_I2S_SLAVE_GET_CLASS(s);
+
+ return sc->init ? sc->init(s) : 0;
+}
+
+static void exynos4210_i2s_slave_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+ k->init = exynos4210_i2s_slave_qdev_init;
+ k->bus_info = &exynos4210_i2s_bus_info;
+}
+
+static TypeInfo exynos4210_i2s_slave_type_info = {
+ .name = TYPE_EXYNOS4210_I2S_SLAVE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(Exynos4210I2SSlave),
+ .abstract = true,
+ .class_size = sizeof(Exynos4210I2SSlaveClass),
+ .class_init = exynos4210_i2s_slave_class_init,
+};
+
+static void exynos4210_i2s_slave_register_types(void)
+{
+ type_register_static(&exynos4210_i2s_slave_type_info);
+}
+
+type_init(exynos4210_i2s_slave_register_types)
--- /dev/null
+#ifndef QEMU_HW_EXYNOS4210_I2S_H
+#define QEMU_HW_EXYNOS4210_I2S_H
+
+#include "qdev.h"
+
+#define EXYNOS4210_I2S_WORD_LEN 4
+
+typedef struct Exynos4210I2SSlave Exynos4210I2SSlave;
+
+#define TYPE_EXYNOS4210_I2S_SLAVE "exynos4210.i2s-slave"
+#define EXYNOS4210_I2S_SLAVE(obj) \
+ OBJECT_CHECK(Exynos4210I2SSlave, (obj), TYPE_EXYNOS4210_I2S_SLAVE)
+#define EXYNOS4210_I2S_SLAVE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(Exynos4210I2SSlaveClass, (klass), \
+ TYPE_EXYNOS4210_I2S_SLAVE)
+#define EXYNOS4210_I2S_SLAVE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(Exynos4210I2SSlaveClass, (obj), TYPE_EXYNOS4210_I2S_SLAVE)
+
+typedef struct {
+ DeviceClass parent_class;
+
+ int (*init)(Exynos4210I2SSlave *s);
+
+ void (*dma_enable)(Exynos4210I2SSlave *s, bool enable);
+} Exynos4210I2SSlaveClass;
+
+struct Exynos4210I2SSlave {
+ DeviceState qdev;
+};
+
+BusState *exynos4210_i2s_bus_new(const char *name,
+ target_phys_addr_t addr,
+ qemu_irq irq);
+
+#define EXYNOS4210_I2S_SLAVE_FROM_QDEV(dev) \
+ DO_UPCAST(Exynos4210I2SSlave, qdev, dev)
+#define FROM_EXYNOS4210_I2S_SLAVE(type, dev) DO_UPCAST(type, i2s, dev)
+
+bool exynos4210_i2s_dma_enabled(BusState *qbus);
+
+uint32_t exynos4210_i2s_dma_get_words_available(BusState *qbus);
+
+void exynos4210_i2s_dma_read(BusState *qbus, void *buf, uint32_t num_words);
+
+void exynos4210_i2s_dma_advance(BusState *qbus, uint32_t num_words);
+
+extern const VMStateDescription vmstate_exynos4210_i2s_slave;
+
+#define VMSTATE_EXYNOS4210_I2S_SLAVE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(Exynos4210I2SSlave), \
+ .vmsd = &vmstate_exynos4210_i2s_slave, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, Exynos4210I2SSlave), \
+}
+
+#endif
--- /dev/null
+/*
+ * Samsung exynos4210 Real Time Clock
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Ogurtsov Oleg <o.ogurtsov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Description:
+ * Register RTCCON:
+ * CLKSEL Bit[1] not used
+ * CLKOUTEN Bit[9] not used
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+#include "exynos4210.h"
+
+#define DEBUG_RTC 0
+/* Get time from host */
+#define EXYNOS4210_RTC_GETSYSTIME 1
+
+#if DEBUG_RTC
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
+
+#define INTP 0x0030
+#define RTCCON 0x0040
+#define TICCNT 0x0044
+#define RTCALM 0x0050
+#define ALMSEC 0x0054
+#define ALMMIN 0x0058
+#define ALMHOUR 0x005C
+#define ALMDAY 0x0060
+#define ALMMON 0x0064
+#define ALMYEAR 0x0068
+#define BCDSEC 0x0070
+#define BDCMIN 0x0074
+#define BCDHOUR 0x0078
+#define BCDDAY 0x007C
+#define BCDDAYWEEK 0x0080
+#define BCDMON 0x0084
+#define BCDYEAR 0x0088
+#define CURTICNT 0x0090
+
+#define TICK_TIMER_ENABLE 0x0100
+#define TICNT_THRESHHOLD 2
+
+
+#define RTC_ENABLE 0x0001
+
+#define INTP_TICK_ENABLE 0x0001
+#define INTP_ALM_ENABLE 0x0002
+
+#define ALARM_INT_ENABLE 0x0040
+
+#define RTC_BASE_FREQ 32768
+
+typedef struct Exynos4210RTCState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* registers */
+ uint32_t reg_intp;
+ uint32_t reg_rtccon;
+ uint32_t reg_ticcnt;
+ uint32_t reg_rtcalm;
+ uint32_t reg_almsec;
+ uint32_t reg_almmin;
+ uint32_t reg_almhour;
+ uint32_t reg_almday;
+ uint32_t reg_almmon;
+ uint32_t reg_almyear;
+ uint32_t reg_curticcnt;
+
+ ptimer_state *ptimer; /* tick timer */
+ ptimer_state *ptimer_1Hz; /* clock timer */
+ uint32_t freq;
+
+ qemu_irq tick_irq; /* Time Tick Generator irq */
+ qemu_irq alm_irq; /* alarm irq */
+
+ struct tm current_tm; /* current time */
+} Exynos4210RTCState;
+
+#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_rtc_state = {
+ .name = "exynos4210.rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
+ VMSTATE_UINT32(freq, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static inline int rtc_to_bcd(int a, int count)
+{
+ int ret = (((a % 100) / 10) << 4) | (a % 10);
+ if (count == 3) {
+ ret |= (((a % 1000) / 100) << 8);
+ }
+ return ret;
+}
+
+static inline int rtc_from_bcd(uint32_t a, int count)
+{
+ int ret = (((a >> 4) & 0x0f) * 10) + (a & 0x0f);
+ if (count == 3) {
+ ret += (((a >> 8) & 0x0f) * 100);
+ }
+ return ret;
+}
+
+static void check_alarm_raise(Exynos4210RTCState *s)
+{
+ unsigned int alarm_raise = 0;
+ struct tm stm = s->current_tm;
+
+ if ((s->reg_rtcalm & 0x01) &&
+ (rtc_to_bcd(stm.tm_sec, 2) == s->reg_almsec)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x02) &&
+ (rtc_to_bcd(stm.tm_min, 2) == s->reg_almmin)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x04) &&
+ (rtc_to_bcd(stm.tm_hour, 2) == s->reg_almhour)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x08) &&
+ (rtc_to_bcd(stm.tm_mday, 2) == s->reg_almday)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x10) &&
+ (rtc_to_bcd(stm.tm_mon, 2) == s->reg_almmon)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x20) &&
+ (rtc_to_bcd(stm.tm_year, 3) == s->reg_almyear)) {
+ alarm_raise = 1;
+ }
+
+ if (alarm_raise) {
+ DPRINTF("ALARM IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_ALM_ENABLE;
+ qemu_irq_raise(s->alm_irq);
+ }
+}
+
+/*
+ * RTC update frequency
+ * Parameters:
+ * reg_value - current RTCCON register or his new value
+ */
+static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
+ uint32_t reg_value)
+{
+ uint32_t freq;
+
+ freq = s->freq;
+ /* set frequncy for time generator */
+ s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
+
+ if (freq != s->freq) {
+ ptimer_set_freq(s->ptimer, s->freq);
+ DPRINTF("freq=%dHz\n", s->freq);
+ }
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+ if ((unsigned)month >= 12) {
+ return 31;
+ }
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
+ d++;
+ }
+ }
+ return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7) {
+ tm->tm_wday = 0;
+ }
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * tick handler
+ */
+static void exynos4210_rtc_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ DPRINTF("TICK IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_TICK_ENABLE;
+ /* raise IRQ */
+ qemu_irq_raise(s->tick_irq);
+
+ /* restart timer */
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+}
+
+/*
+ * 1Hz clock handler
+ */
+static void exynos4210_rtc_1Hz_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ rtc_next_second(&s->current_tm);
+ /* DPRINTF("1Hz tick\n"); */
+
+ /* raise IRQ */
+ if (s->reg_rtcalm & ALARM_INT_ENABLE) {
+ check_alarm_raise(s);
+ }
+
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+}
+
+/*
+ * RTC Read
+ */
+static uint64_t exynos4210_rtc_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ uint32_t value = 0;
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ value = s->reg_intp;
+ break;
+ case RTCCON:
+ value = s->reg_rtccon;
+ break;
+ case TICCNT:
+ value = s->reg_ticcnt;
+ break;
+ case RTCALM:
+ value = s->reg_rtcalm;
+ break;
+ case ALMSEC:
+ value = s->reg_almsec;
+ break;
+ case ALMMIN:
+ value = s->reg_almmin;
+ break;
+ case ALMHOUR:
+ value = s->reg_almhour;
+ break;
+ case ALMDAY:
+ value = s->reg_almday;
+ break;
+ case ALMMON:
+ value = s->reg_almmon;
+ break;
+ case ALMYEAR:
+ value = s->reg_almyear;
+ break;
+
+ case BCDSEC:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_sec, 2);
+ break;
+ case BDCMIN:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_min, 2);
+ break;
+ case BCDHOUR:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_hour, 2);
+ break;
+ case BCDDAYWEEK:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_wday, 2);
+ break;
+ case BCDDAY:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mday, 2);
+ break;
+ case BCDMON:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mon + 1, 2);
+ break;
+ case BCDYEAR:
+ value = (uint32_t)rtc_to_bcd(s->current_tm.tm_year, 3);
+ break;
+
+ case CURTICNT:
+ s->reg_curticcnt = ptimer_get_count(s->ptimer);
+ value = s->reg_curticcnt;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+ }
+ return value;
+}
+
+/*
+ * RTC Write
+ */
+static void exynos4210_rtc_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ if (value & INTP_ALM_ENABLE) {
+ qemu_irq_lower(s->alm_irq);
+ s->reg_intp &= (~INTP_ALM_ENABLE);
+ }
+ if (value & INTP_TICK_ENABLE) {
+ qemu_irq_lower(s->tick_irq);
+ s->reg_intp &= (~INTP_TICK_ENABLE);
+ }
+ break;
+ case RTCCON:
+ if (value & RTC_ENABLE) {
+ exynos4210_rtc_update_freq(s, value);
+ }
+ if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
+ /* clock timer */
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+ DPRINTF("run clock timer\n");
+ }
+ if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
+ /* tick timer */
+ ptimer_stop(s->ptimer);
+ /* clock timer */
+ ptimer_stop(s->ptimer_1Hz);
+ DPRINTF("stop all timers\n");
+ }
+ if (value & RTC_ENABLE) {
+ if ((value & TICK_TIMER_ENABLE) >
+ (s->reg_rtccon & TICK_TIMER_ENABLE) &&
+ (s->reg_ticcnt)) {
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+ DPRINTF("run tick timer\n");
+ }
+ if ((value & TICK_TIMER_ENABLE) <
+ (s->reg_rtccon & TICK_TIMER_ENABLE)) {
+ ptimer_stop(s->ptimer);
+ }
+ }
+ s->reg_rtccon = value;
+ break;
+ case TICCNT:
+ if (value > TICNT_THRESHHOLD) {
+ s->reg_ticcnt = value;
+ } else {
+ fprintf(stderr,
+ "[exynos4210.rtc: bad TICNT value %u ]\n",
+ (uint32_t)value);
+ }
+ break;
+
+ case RTCALM:
+ s->reg_rtcalm = value;
+ break;
+ case ALMSEC:
+ s->reg_almsec = (value & 0x7f);
+ break;
+ case ALMMIN:
+ s->reg_almmin = (value & 0x7f);
+ break;
+ case ALMHOUR:
+ s->reg_almhour = (value & 0x3f);
+ break;
+ case ALMDAY:
+ s->reg_almday = (value & 0x3f);
+ break;
+ case ALMMON:
+ s->reg_almmon = (value & 0x1f);
+ break;
+ case ALMYEAR:
+ s->reg_almyear = (value & 0x0fff);
+ break;
+
+ case BCDSEC:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_sec = rtc_from_bcd(value, 2);
+ }
+ break;
+ case BDCMIN:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_min = rtc_from_bcd(value, 2);
+ }
+ break;
+ case BCDHOUR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_hour = rtc_from_bcd(value, 2);
+ }
+ break;
+ case BCDDAYWEEK:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_wday = rtc_from_bcd(value, 2);
+ }
+ break;
+ case BCDDAY:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mday = rtc_from_bcd(value, 2);
+ }
+ break;
+ case BCDMON:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mon = rtc_from_bcd(value, 2) - 1;
+ }
+ break;
+ case BCDYEAR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_year = rtc_from_bcd(value, 3);
+ }
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+
+ }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_rtc_reset(DeviceState *d)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)d;
+
+#if EXYNOS4210_RTC_GETSYSTIME
+ time_t ct;
+
+ time(&ct);
+#ifndef _WIN32
+ gmtime_r(&ct, &s->current_tm);
+ DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
+ s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
+ s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
+#endif
+#endif
+
+ s->reg_intp = 0;
+ s->reg_rtccon = 0;
+ s->reg_ticcnt = 0;
+ s->reg_rtcalm = 0;
+ s->reg_almsec = 0;
+ s->reg_almmin = 0;
+ s->reg_almhour = 0;
+ s->reg_almday = 0;
+ s->reg_almmon = 0;
+ s->reg_almyear = 0;
+
+ s->reg_curticcnt = 0;
+
+ exynos4210_rtc_update_freq(s, s->reg_rtccon);
+ ptimer_stop(s->ptimer);
+ ptimer_stop(s->ptimer_1Hz);
+}
+
+static const MemoryRegionOps exynos4210_rtc_ops = {
+ .read = exynos4210_rtc_read,
+ .write = exynos4210_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * RTC timer initialization
+ */
+static int exynos4210_rtc_init(SysBusDevice *dev)
+{
+ Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
+ QEMUBH *bh;
+
+ bh = qemu_bh_new(exynos4210_rtc_tick, s);
+ s->ptimer = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
+ exynos4210_rtc_update_freq(s, 0);
+
+ bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
+ s->ptimer_1Hz = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
+
+ sysbus_init_irq(dev, &s->alm_irq);
+ sysbus_init_irq(dev, &s->tick_irq);
+
+ memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
+ EXYNOS4210_RTC_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_rtc_init;
+ dc->reset = exynos4210_rtc_reset;
+ dc->vmsd = &vmstate_exynos4210_rtc_state;
+}
+
+static TypeInfo exynos4210_rtc_info = {
+ .name = "exynos4210.rtc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210RTCState),
+ .class_init = exynos4210_rtc_class_init,
+};
+
+static void exynos4210_rtc_register_types(void)
+{
+ type_register_static(&exynos4210_rtc_info);
+}
+
+type_init(exynos4210_rtc_register_types)
#include "exynos4210.h"
-#undef DEBUG_UART
-#undef DEBUG_UART_EXTEND
-#undef DEBUG_IRQ
-#undef DEBUG_Rx_DATA
-#undef DEBUG_Tx_DATA
-
#define DEBUG_UART 0
#define DEBUG_UART_EXTEND 0
#define DEBUG_IRQ 0
#define DEBUG_Rx_DATA 0
#define DEBUG_Tx_DATA 0
-#if DEBUG_UART
+#if DEBUG_UART || DEBUG_UART_EXTEND
#define PRINT_DEBUG(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
#define UERSTAT_FRAME 0x4
#define UERSTAT_BREAK 0x8
+#define TYPE_EXYNOS4210_UART "exynos4210.uart"
+
typedef struct {
uint8_t *data;
uint32_t sp, rp; /* store and retrieve pointers */
} Exynos4210UartState;
-#if DEBUG_UART
+#if DEBUG_UART || DEBUG_UART_EXTEND
/* Used only for debugging inside PRINT_DEBUG_... macros */
static const char *exynos4210_uart_regname(target_phys_addr_t offset)
{
uint64_t uclk_rate;
if (s->reg[I_(UBRDIV)] == 0) {
+ PRINT_DEBUG("Baud rate division value is 0\n");
return;
}
frame_size += data_bits + stop_bits;
- uclk_rate = 24000000;
+ switch (s->channel) {
+ case 0:
+ uclk_rate = exynos4210_cmu_get_rate(EXYNOS4210_SCLK_UART0); break;
+ case 1:
+ uclk_rate = exynos4210_cmu_get_rate(EXYNOS4210_SCLK_UART1); break;
+ case 2:
+ uclk_rate = exynos4210_cmu_get_rate(EXYNOS4210_SCLK_UART2); break;
+ case 3:
+ uclk_rate = exynos4210_cmu_get_rate(EXYNOS4210_SCLK_UART3); break;
+ default:
+ hw_error("%s: Incorrect UART channel: %d\n",
+ __func__, s->channel);
+ }
speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
(s->reg[I_(UFRACVAL)] & 0x7) + 16);
s->channel, speed, parity, data_bits, stop_bits);
}
+static void uclk_rate_changed(void *opaque)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+
+ PRINT_DEBUG("Clock sclk_uart%d was changed\n", s->channel);
+
+ exynos4210_uart_update_parameters(s);
+}
+
static void exynos4210_uart_write(void *opaque, target_phys_addr_t offset,
uint64_t val, unsigned size)
{
container_of(dev, Exynos4210UartState, busdev.qdev);
int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
int i;
+ Exynos4210Clock clock_id;
for (i = 0; i < regs_number; i++) {
s->reg[I_(exynos4210_uart_regs[i].offset)] =
fifo_reset(&s->rx);
fifo_reset(&s->tx);
+ switch (s->channel) {
+ case 0: clock_id = EXYNOS4210_SCLK_UART0; break;
+ case 1: clock_id = EXYNOS4210_SCLK_UART1; break;
+ case 2: clock_id = EXYNOS4210_SCLK_UART2; break;
+ case 3: clock_id = EXYNOS4210_SCLK_UART3; break;
+ default:
+ hw_error("Wrong channel number: %d.\n", s->channel);
+ }
+
+ exynos4210_register_clock_handler(uclk_rate_changed, clock_id, s);
+
PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
}
return 0;
}
+
static Property exynos4210_uart_properties[] = {
DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
}
static TypeInfo exynos4210_uart_info = {
- .name = "exynos4210.uart",
+ .name = TYPE_EXYNOS4210_UART,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210UartState),
.class_init = exynos4210_uart_class_init,
--- /dev/null
+/*
+ * Samsung exynos4210 wm8994 driver
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Vorobiov Stanislav <s.vorobiov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "wm8994.h"
+#include "hw.h"
+#include "i2c.h"
+#include "audio/audio.h"
+
+/* #define DEBUG_WM8994 */
+
+#ifdef DEBUG_WM8994
+#define DPRINTF(fmt, ...) \
+ do { \
+ fprintf(stdout, "WM8994: [%s:%d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); \
+ } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+/* Registers */
+#define WM8994_SOFTWARE_RESET 0x00
+#define WM8994_POWER_MANAGEMENT_2 0x02
+#define WM8994_SPEAKER_VOLUME_LEFT 0x26 /* Speaker */
+#define WM8994_SPEAKER_VOLUME_RIGHT 0x27
+#define WM8994_CHIP_REVISION 0x100
+#define WM8994_AIF1_RATE 0x210
+#define WM8994_AIF1_CONTROL_1 0x300
+#define WM8994_GPIO_1 0x700
+#define WM8994_GPIO_3 0x702
+#define WM8994_GPIO_4 0x703
+#define WM8994_GPIO_5 0x704
+#define WM8994_GPIO_6 0x705
+#define WM8994_GPIO_7 0x706
+#define WM8994_GPIO_8 0x707
+#define WM8994_GPIO_9 0x708
+#define WM8994_GPIO_10 0x709
+#define WM8994_GPIO_11 0x70A
+#define WM8994_PULL_CONTROL_2 0x721
+#define WM8994_INPUT_MIXER_1 0x15
+#define WM8994_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8994_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8994_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8994_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8994_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8994_LEFT_OPGA_VOLUME 0x20
+#define WM8994_RIGHT_OPGA_VOLUME 0x21
+#define WM8994_LINE_MIXER_1 0x34
+#define WM8994_LINE_MIXER_2 0x35
+#define WM8994_ANTIPOP_1 0x38
+#define WM8994_LDO_1 0x3B
+#define WM8994_LDO_2 0x3C
+#define WM8994_AIF1_ADC1_LEFT_VOLUME 0x400
+#define WM8994_AIF1_ADC1_RIGHT_VOLUME 0x401
+#define WM8994_AIF1_DAC1_LEFT_VOLUME 0x402
+#define WM8994_AIF1_DAC1_RIGHT_VOLUME 0x403
+#define WM8994_AIF1_ADC2_LEFT_VOLUME 0x404
+#define WM8994_AIF1_ADC2_RIGHT_VOLUME 0x405
+#define WM8994_AIF1_DAC2_LEFT_VOLUME 0x406
+#define WM8994_AIF1_DAC2_RIGHT_VOLUME 0x407
+#define WM8994_AIF1_DAC1_FILTERS_2 0x421
+#define WM8994_AIF1_DAC2_FILTERS_2 0x423
+#define WM8994_AIF2_ADC_LEFT_VOLUME 0x500
+#define WM8994_AIF2_ADC_RIGHT_VOLUME 0x501
+#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502
+#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503
+#define WM8994_AIF2_DAC_FILTERS_2 0x521
+#define WM8994_DAC1_LEFT_VOLUME 0x610
+#define WM8994_DAC1_RIGHT_VOLUME 0x611
+#define WM8994_DAC2_LEFT_VOLUME 0x612
+#define WM8994_DAC2_RIGHT_VOLUME 0x613
+#define WM8994_POWER_MANAGEMENT_1 0x01
+#define WM8994_POWER_MANAGEMENT_3 0x03
+#define WM8994_ANTIPOP_2 0x39
+#define WM8994_AIF1_CLOCKING_1 0x200
+#define WM8994_FLL1_CONTROL_1 0x220
+#define WM8994_FLL1_CONTROL_2 0x221
+#define WM8994_FLL1_CONTROL_3 0x222
+#define WM8994_FLL1_CONTROL_4 0x223
+#define WM8994_FLL1_CONTROL_5 0x224
+#define WM8994_AIF1_MASTER_SLAVE 0x302
+#define WM8994_AIF1_BCLK 0x303
+#define WM8994_AIF1DAC_LRCLK 0x305
+#define WM8994_AIF1_DAC1_FILTERS_1 0x420
+
+#define WM8994_REGS_NUM 0x74A
+
+/*
+ * R528 (0x210) - AIF1 Rate
+ */
+#define WM8994_AIF1_SR_MASK 0x00F0 /* AIF1_SR - [7:4] */
+#define WM8994_AIF1_SR_SHIFT 4 /* AIF1_SR - [7:4] */
+#define WM8994_AIF1_SR_WIDTH 4 /* AIF1_SR - [7:4] */
+#define WM8994_AIF1CLK_RATE_MASK 0x000F /* AIF1CLK_RATE - [3:0] */
+#define WM8994_AIF1CLK_RATE_SHIFT 0 /* AIF1CLK_RATE - [3:0] */
+#define WM8994_AIF1CLK_RATE_WIDTH 4 /* AIF1CLK_RATE - [3:0] */
+
+/*
+ * R768 (0x300) - AIF1 Control (1)
+ */
+#define WM8994_AIF1ADCL_SRC 0x8000 /* AIF1ADCL_SRC */
+#define WM8994_AIF1ADCL_SRC_MASK 0x8000 /* AIF1ADCL_SRC */
+#define WM8994_AIF1ADCL_SRC_SHIFT 15 /* AIF1ADCL_SRC */
+#define WM8994_AIF1ADCL_SRC_WIDTH 1 /* AIF1ADCL_SRC */
+#define WM8994_AIF1ADCR_SRC 0x4000 /* AIF1ADCR_SRC */
+#define WM8994_AIF1ADCR_SRC_MASK 0x4000 /* AIF1ADCR_SRC */
+#define WM8994_AIF1ADCR_SRC_SHIFT 14 /* AIF1ADCR_SRC */
+#define WM8994_AIF1ADCR_SRC_WIDTH 1 /* AIF1ADCR_SRC */
+#define WM8994_AIF1ADC_TDM 0x2000 /* AIF1ADC_TDM */
+#define WM8994_AIF1ADC_TDM_MASK 0x2000 /* AIF1ADC_TDM */
+#define WM8994_AIF1ADC_TDM_SHIFT 13 /* AIF1ADC_TDM */
+#define WM8994_AIF1ADC_TDM_WIDTH 1 /* AIF1ADC_TDM */
+#define WM8994_AIF1_BCLK_INV 0x0100 /* AIF1_BCLK_INV */
+#define WM8994_AIF1_BCLK_INV_MASK 0x0100 /* AIF1_BCLK_INV */
+#define WM8994_AIF1_BCLK_INV_SHIFT 8 /* AIF1_BCLK_INV */
+#define WM8994_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8994_AIF1_LRCLK_INV 0x0080 /* AIF1_LRCLK_INV */
+#define WM8994_AIF1_LRCLK_INV_MASK 0x0080 /* AIF1_LRCLK_INV */
+#define WM8994_AIF1_LRCLK_INV_SHIFT 7 /* AIF1_LRCLK_INV */
+#define WM8994_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
+#define WM8994_AIF1_WL_MASK 0x0060 /* AIF1_WL - [6:5] */
+#define WM8994_AIF1_WL_SHIFT 5 /* AIF1_WL - [6:5] */
+#define WM8994_AIF1_WL_WIDTH 2 /* AIF1_WL - [6:5] */
+#define WM8994_AIF1_FMT_MASK 0x0018 /* AIF1_FMT - [4:3] */
+#define WM8994_AIF1_FMT_SHIFT 3 /* AIF1_FMT - [4:3] */
+#define WM8994_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [4:3] */
+
+/*
+ * R1056 (0x420) - AIF1 DAC1 Filters (1)
+ */
+#define WM8994_AIF1DAC1_MUTE 0x0200 /* AIF1DAC1_MUTE */
+#define WM8994_AIF1DAC1_MUTE_MASK 0x0200 /* AIF1DAC1_MUTE */
+#define WM8994_AIF1DAC1_MUTE_SHIFT 9 /* AIF1DAC1_MUTE */
+#define WM8994_AIF1DAC1_MUTE_WIDTH 1 /* AIF1DAC1_MUTE */
+#define WM8994_AIF1DAC1_MONO 0x0080 /* AIF1DAC1_MONO */
+#define WM8994_AIF1DAC1_MONO_MASK 0x0080 /* AIF1DAC1_MONO */
+#define WM8994_AIF1DAC1_MONO_SHIFT 7 /* AIF1DAC1_MONO */
+#define WM8994_AIF1DAC1_MONO_WIDTH 1 /* AIF1DAC1_MONO */
+#define WM8994_AIF1DAC1_MUTERATE 0x0020 /* AIF1DAC1_MUTERATE */
+#define WM8994_AIF1DAC1_MUTERATE_MASK 0x0020 /* AIF1DAC1_MUTERATE */
+#define WM8994_AIF1DAC1_MUTERATE_SHIFT 5 /* AIF1DAC1_MUTERATE */
+#define WM8994_AIF1DAC1_MUTERATE_WIDTH 1 /* AIF1DAC1_MUTERATE */
+#define WM8994_AIF1DAC1_UNMUTE_RAMP 0x0010 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8994_AIF1DAC1_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8994_AIF1DAC1_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8994_AIF1DAC1_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8994_AIF1DAC1_DEEMP_MASK 0x0006 /* AIF1DAC1_DEEMP - [2:1] */
+#define WM8994_AIF1DAC1_DEEMP_SHIFT 1 /* AIF1DAC1_DEEMP - [2:1] */
+#define WM8994_AIF1DAC1_DEEMP_WIDTH 2 /* AIF1DAC1_DEEMP - [2:1] */
+
+/*
+ * R38 (0x26), R39 (0x27) - Speaker Volume
+ */
+#define WM8994_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8994_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8994_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8994_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8994_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM8994_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM8994_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM8994_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM8994_SPKOUTL_MUTE_N 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8994_SPKOUTL_MUTE_N_MASK 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8994_SPKOUTL_MUTE_N_SHIFT 6 /* SPKOUTL_MUTE_N */
+#define WM8994_SPKOUTL_MUTE_N_WIDTH 1 /* SPKOUTL_MUTE_N */
+#define WM8994_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */
+#define WM8994_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */
+#define WM8994_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */
+
+#define CODEC "wm8994"
+
+typedef struct {
+ const char *name;
+ uint16_t address;
+} WM8994Reg;
+
+#ifdef DEBUG_WM8994
+static WM8994Reg wm8994_regs[] = {
+ {"SOFTWARE_RESET", WM8994_SOFTWARE_RESET },
+ {"POWER_MANAGEMENT_2", WM8994_POWER_MANAGEMENT_2 },
+ {"SPEAKER_VOLUME_LEFT", WM8994_SPEAKER_VOLUME_LEFT },
+ {"SPEAKER_VOLUME_RIGHT", WM8994_SPEAKER_VOLUME_RIGHT },
+ {"CHIP_REVISION", WM8994_CHIP_REVISION },
+ {"AIF1_RATE", WM8994_AIF1_RATE },
+ {"AIF1_CONTROL_1", WM8994_AIF1_CONTROL_1 },
+ {"GPIO_1", WM8994_GPIO_1 },
+ {"GPIO_3", WM8994_GPIO_3 },
+ {"GPIO_4", WM8994_GPIO_4 },
+ {"GPIO_5", WM8994_GPIO_5 },
+ {"GPIO_6", WM8994_GPIO_6 },
+ {"GPIO_7", WM8994_GPIO_7 },
+ {"GPIO_8", WM8994_GPIO_8 },
+ {"GPIO_9", WM8994_GPIO_9 },
+ {"GPIO_10", WM8994_GPIO_10 },
+ {"GPIO_11", WM8994_GPIO_11 },
+ {"PULL_CONTROL_2", WM8994_PULL_CONTROL_2 },
+ {"INPUT_MIXER_1", WM8994_INPUT_MIXER_1 },
+ {"LEFT_LINE_INPUT_1_2_VOLUME", WM8994_LEFT_LINE_INPUT_1_2_VOLUME },
+ {"LEFT_LINE_INPUT_3_4_VOLUME", WM8994_LEFT_LINE_INPUT_3_4_VOLUME },
+ {"RIGHT_LINE_INPUT_1_2_VOLUME", WM8994_RIGHT_LINE_INPUT_1_2_VOLUME },
+ {"RIGHT_LINE_INPUT_3_4_VOLUME", WM8994_RIGHT_LINE_INPUT_3_4_VOLUME },
+ {"LEFT_OUTPUT_VOLUME", WM8994_LEFT_OUTPUT_VOLUME },
+ {"RIGHT_OUTPUT_VOLUME", WM8994_RIGHT_OUTPUT_VOLUME },
+ {"LEFT_OPGA_VOLUME", WM8994_LEFT_OPGA_VOLUME },
+ {"RIGHT_OPGA_VOLUME", WM8994_RIGHT_OPGA_VOLUME },
+ {"LINE_MIXER_1", WM8994_LINE_MIXER_1 },
+ {"LINE_MIXER_2", WM8994_LINE_MIXER_2 },
+ {"ANTIPOP_1", WM8994_ANTIPOP_1 },
+ {"LDO_1", WM8994_LDO_1 },
+ {"LDO_2", WM8994_LDO_2 },
+ {"AIF1_ADC1_LEFT_VOLUME", WM8994_AIF1_ADC1_LEFT_VOLUME },
+ {"AIF1_ADC1_RIGHT_VOLUME", WM8994_AIF1_ADC1_RIGHT_VOLUME },
+ {"AIF1_DAC1_LEFT_VOLUME", WM8994_AIF1_DAC1_LEFT_VOLUME },
+ {"AIF1_DAC1_RIGHT_VOLUME", WM8994_AIF1_DAC1_RIGHT_VOLUME },
+ {"AIF1_ADC2_LEFT_VOLUME", WM8994_AIF1_ADC2_LEFT_VOLUME },
+ {"AIF1_ADC2_RIGHT_VOLUME", WM8994_AIF1_ADC2_RIGHT_VOLUME },
+ {"AIF1_DAC2_LEFT_VOLUME", WM8994_AIF1_DAC2_LEFT_VOLUME },
+ {"AIF1_DAC2_RIGHT_VOLUME", WM8994_AIF1_DAC2_RIGHT_VOLUME },
+ {"AIF1_DAC1_FILTERS_2", WM8994_AIF1_DAC1_FILTERS_2 },
+ {"AIF1_DAC2_FILTERS_2", WM8994_AIF1_DAC2_FILTERS_2 },
+ {"AIF2_ADC_LEFT_VOLUME", WM8994_AIF2_ADC_LEFT_VOLUME },
+ {"AIF2_ADC_RIGHT_VOLUME", WM8994_AIF2_ADC_RIGHT_VOLUME },
+ {"AIF2_DAC_LEFT_VOLUME", WM8994_AIF2_DAC_LEFT_VOLUME },
+ {"AIF2_DAC_RIGHT_VOLUME", WM8994_AIF2_DAC_RIGHT_VOLUME },
+ {"AIF2_DAC_FILTERS_2", WM8994_AIF2_DAC_FILTERS_2 },
+ {"DAC1_LEFT_VOLUME", WM8994_DAC1_LEFT_VOLUME },
+ {"DAC1_RIGHT_VOLUME", WM8994_DAC1_RIGHT_VOLUME },
+ {"DAC2_LEFT_VOLUME", WM8994_DAC2_LEFT_VOLUME },
+ {"DAC2_RIGHT_VOLUME", WM8994_DAC2_RIGHT_VOLUME },
+ {"POWER_MANAGEMENT_1", WM8994_POWER_MANAGEMENT_1 },
+ {"POWER_MANAGEMENT_3", WM8994_POWER_MANAGEMENT_3 },
+ {"ANTIPOP_2", WM8994_ANTIPOP_2 },
+ {"AIF1_CLOCKING_1", WM8994_AIF1_CLOCKING_1 },
+ {"FLL1_CONTROL_1", WM8994_FLL1_CONTROL_1 },
+ {"FLL1_CONTROL_2", WM8994_FLL1_CONTROL_2 },
+ {"FLL1_CONTROL_3", WM8994_FLL1_CONTROL_3 },
+ {"FLL1_CONTROL_4", WM8994_FLL1_CONTROL_4 },
+ {"FLL1_CONTROL_5", WM8994_FLL1_CONTROL_5 },
+ {"AIF1_MASTER_SLAVE", WM8994_AIF1_MASTER_SLAVE },
+ {"AIF1_BCLK", WM8994_AIF1_BCLK },
+ {"AIF1DAC_LRCLK", WM8994_AIF1DAC_LRCLK },
+ {"AIF1_DAC1_FILTERS_1", WM8994_AIF1_DAC1_FILTERS_1 },
+};
+#endif
+
+typedef struct {
+ I2CSlave i2c;
+ uint16_t i2c_addr;
+ int i2c_idx;
+
+ uint16_t reg[WM8994_REGS_NUM];
+
+ void (*data_req)(void *, int);
+ void *opaque;
+
+ SWVoiceOut *dac_voice;
+ QEMUSoundCard card;
+
+ bool active;
+} WM8994State;
+
+static struct {
+ int val, rate;
+} srs[] = {
+ { 0, 8000 },
+ { 1, 11025 },
+ { 2, 12000 },
+ { 3, 16000 },
+ { 4, 22050 },
+ { 5, 24000 },
+ { 6, 32000 },
+ { 7, 44100 },
+ { 8, 48000 },
+ { 9, 88200 },
+ { 10, 96000 },
+};
+
+#ifdef DEBUG_WM8994
+static char wm8994_regname_buff[50];
+static const char *wm8994_regname(WM8994State *s,
+ uint16_t address)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994_regs); i++) {
+ if (address == wm8994_regs[i].address) {
+ return wm8994_regs[i].name;
+ }
+ }
+
+ snprintf(
+ &wm8994_regname_buff[0],
+ sizeof(wm8994_regname_buff),
+ "reg(%.4X)",
+ (unsigned int)address);
+
+ return &wm8994_regname_buff[0];
+}
+#endif
+
+static void wm8994_update_format(WM8994State *s);
+
+static int vmstate_wm8994_post_load(void *opaque, int version)
+{
+ WM8994State *s = opaque;
+
+ DPRINTF("enter\n");
+
+ wm8994_update_format(s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_wm8994 = {
+ .name = CODEC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = vmstate_wm8994_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_I2C_SLAVE(i2c, WM8994State),
+ VMSTATE_UINT16(i2c_addr, WM8994State),
+ VMSTATE_INT32(i2c_idx, WM8994State),
+ VMSTATE_UINT16_ARRAY(reg, WM8994State,
+ WM8994_REGS_NUM),
+ VMSTATE_BOOL(active, WM8994State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static uint8_t wm8994_volume(WM8994State *s, uint16_t reg)
+{
+ return (s->reg[reg] >> WM8994_SPKOUTL_VOL_SHIFT) &
+ (WM8994_SPKOUTL_VOL_MASK >> WM8994_SPKOUTL_VOL_SHIFT);
+}
+
+static int wm8994_rate(WM8994State *s, uint16_t reg)
+{
+ int i;
+ int rate = (s->reg[reg] >> WM8994_AIF1_SR_SHIFT) &
+ (WM8994_AIF1_SR_MASK >> WM8994_AIF1_SR_SHIFT);
+
+ for (i = 0; i < ARRAY_SIZE(srs); ++i) {
+ if (rate == srs[i].val) {
+ return srs[i].rate;
+ }
+ }
+
+ return 0;
+}
+
+static audfmt_e wm8994_wl(WM8994State *s, uint16_t reg)
+{
+ int wl = (s->reg[reg] >> WM8994_AIF1_WL_SHIFT) &
+ (WM8994_AIF1_WL_MASK >> WM8994_AIF1_WL_SHIFT);
+
+ switch (wl) {
+ case 0: return AUD_FMT_S16;
+ case 3: return AUD_FMT_S32;
+ case 1:
+ /*
+ * Unsupported format (20 bits per channel), but kernel
+ * sets this sometimes when not playing anything
+ */
+ return AUD_FMT_S16;
+ case 2:
+ /*
+ * Unsupported format (24 bits per channel), but kernel
+ * sets this sometimes when not playing anything
+ */
+ return AUD_FMT_S16;
+ default:
+ hw_error("Unknown format\n");
+ }
+}
+
+static void wm8994_audio_out_cb(void *opaque, int free_b)
+{
+ WM8994State *s = (WM8994State *) opaque;
+
+ if (s->data_req) {
+ s->data_req(s->opaque, free_b);
+ }
+}
+
+static void wm8994_update_volume(WM8994State *s)
+{
+ int volume_left = wm8994_volume(s, WM8994_SPEAKER_VOLUME_LEFT);
+ int volume_right = wm8994_volume(s, WM8994_SPEAKER_VOLUME_RIGHT);
+
+ if (s->dac_voice) {
+ /* Speaker */
+ AUD_set_volume_out(s->dac_voice, 1, volume_left, volume_right);
+ }
+}
+
+static void wm8994_update_active(WM8994State *s)
+{
+ if (s->dac_voice) {
+ AUD_set_active_out(s->dac_voice, s->active);
+ }
+}
+
+static void wm8994_update_format(WM8994State *s)
+{
+ struct audsettings out_fmt;
+
+ if (s->dac_voice) {
+ AUD_set_active_out(s->dac_voice, 0);
+ }
+
+ /* Setup output */
+ out_fmt.endianness = 0;
+ out_fmt.nchannels = 2;
+ out_fmt.freq = wm8994_rate(s, WM8994_AIF1_RATE);
+ out_fmt.fmt = wm8994_wl(s, WM8994_AIF1_CONTROL_1);
+
+ s->dac_voice =
+ AUD_open_out(&s->card,
+ s->dac_voice,
+ CODEC ".speaker",
+ s,
+ wm8994_audio_out_cb,
+ &out_fmt);
+
+ wm8994_update_volume(s);
+
+ wm8994_update_active(s);
+}
+
+static void wm8994_reset(DeviceState *dev)
+{
+ WM8994State *s = FROM_I2C_SLAVE(WM8994State, I2C_SLAVE_FROM_QDEV(dev));
+
+ DPRINTF("enter\n");
+
+ s->i2c_addr = 0;
+ s->i2c_idx = 0;
+ s->active = 0;
+
+ memset(s->reg, 0, sizeof(s->reg));
+ s->reg[WM8994_SOFTWARE_RESET] = 0x8994;
+ s->reg[WM8994_POWER_MANAGEMENT_2] = 0x6000;
+ s->reg[WM8994_SPEAKER_VOLUME_LEFT] = (0x79 << WM8994_SPKOUTL_VOL_SHIFT);
+ s->reg[WM8994_SPEAKER_VOLUME_RIGHT] = (0x79 << WM8994_SPKOUTL_VOL_SHIFT);
+ s->reg[WM8994_AIF1_RATE] = (srs[0].val << WM8994_AIF1_SR_SHIFT);
+ s->reg[WM8994_AIF1_CONTROL_1] = (0x0 << WM8994_AIF1_WL_SHIFT);
+
+ wm8994_update_format(s);
+}
+
+static int wm8994_tx(I2CSlave *i2c, uint8_t data)
+{
+ WM8994State *s = (WM8994State *) i2c;
+
+ if (s->i2c_idx < sizeof(s->i2c_addr)) {
+ s->i2c_addr |=
+ (uint16_t)data << ((sizeof(s->i2c_addr) - s->i2c_idx - 1) * 8);
+ } else {
+ uint16_t offset = s->i2c_idx - sizeof(s->i2c_addr);
+ uint16_t addr = s->i2c_addr + (offset / 2);
+
+ offset %= 2;
+
+ if (addr >= WM8994_REGS_NUM) {
+ hw_error("illegal write offset 0x%X\n", s->i2c_addr + offset);
+ } else {
+ s->reg[addr] &= ~(0xFF << ((1 - offset) * 8));
+ s->reg[addr] |= (uint16_t)data << ((1 - offset) * 8);
+
+ if (offset == 1) {
+ DPRINTF("0x%.4X -> %s\n",
+ (unsigned int)s->reg[addr],
+ wm8994_regname(s, addr));
+ switch (addr) {
+ case WM8994_SOFTWARE_RESET:
+ wm8994_reset(&s->i2c.qdev);
+ break;
+
+ case WM8994_SPEAKER_VOLUME_LEFT:
+ case WM8994_SPEAKER_VOLUME_RIGHT:
+ wm8994_update_volume(s);
+ break;
+
+ case WM8994_AIF1_RATE:
+ case WM8994_AIF1_CONTROL_1:
+ wm8994_update_format(s);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ ++s->i2c_idx;
+
+ return 1;
+}
+
+static int wm8994_rx(I2CSlave *i2c)
+{
+ WM8994State *s = (WM8994State *) i2c;
+
+ if (s->i2c_idx >= sizeof(uint16_t)) {
+ DPRINTF("too much data requested (%i bytes)\n", (s->i2c_idx + 1));
+
+ return 0;
+ }
+
+ if (s->i2c_addr < WM8994_REGS_NUM) {
+ int ret = (s->reg[s->i2c_addr] >> ((1 - s->i2c_idx) * 8)) & 0xFF;
+
+ if (s->i2c_idx == (sizeof(uint16_t) - 1)) {
+ DPRINTF("%s -> 0x%.4X\n",
+ wm8994_regname(s, s->i2c_addr),
+ (unsigned int)s->reg[s->i2c_addr]);
+ }
+
+ ++s->i2c_idx;
+
+ return ret;
+ } else {
+ hw_error(
+ "wm8994: illegal read offset 0x%x\n", s->i2c_addr + s->i2c_idx);
+ }
+}
+
+static void wm8994_event(I2CSlave *i2c, enum i2c_event event)
+{
+ WM8994State *s = (WM8994State *) i2c;
+
+ switch (event) {
+ case I2C_START_SEND:
+ s->i2c_addr = 0;
+ case I2C_START_RECV:
+ s->i2c_idx = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+void wm8994_set_active(DeviceState *dev, bool active)
+{
+ WM8994State *s = FROM_I2C_SLAVE(WM8994State, I2C_SLAVE_FROM_QDEV(dev));
+
+ DPRINTF("enter %d\n", active);
+
+ s->active = active;
+
+ wm8994_update_active(s);
+}
+
+void wm8994_data_req_set(DeviceState *dev,
+ void (*data_req)(void*, int),
+ void *opaque)
+{
+ WM8994State *s = FROM_I2C_SLAVE(WM8994State, I2C_SLAVE_FROM_QDEV(dev));
+
+ s->data_req = data_req;
+ s->opaque = opaque;
+}
+
+int wm8994_dac_write(DeviceState *dev, void *buf, int num_bytes)
+{
+ WM8994State *s = FROM_I2C_SLAVE(WM8994State, I2C_SLAVE_FROM_QDEV(dev));
+
+ int sent = 0;
+
+ if (!s->dac_voice) {
+ return 0;
+ }
+
+ while (sent < num_bytes) {
+ int ret = AUD_write(s->dac_voice,
+ (uint8_t *)buf + sent,
+ num_bytes - sent);
+
+ sent += ret;
+
+ if (ret == 0) {
+ break;
+ }
+ }
+
+ return sent;
+}
+
+static int wm8994_init(I2CSlave *i2c)
+{
+ WM8994State *s = FROM_I2C_SLAVE(WM8994State, i2c);
+
+ AUD_register_card(CODEC, &s->card);
+
+ return 0;
+}
+
+static void wm8994_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+ sc->init = wm8994_init;
+ sc->event = wm8994_event;
+ sc->recv = wm8994_rx;
+ sc->send = wm8994_tx;
+ dc->reset = wm8994_reset;
+ dc->vmsd = &vmstate_wm8994;
+}
+
+static TypeInfo wm8994_info = {
+ .name = "wm8994",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(WM8994State),
+ .class_init = wm8994_class_init,
+};
+
+static void wm8994_register_types(void)
+{
+ type_register_static(&wm8994_info);
+}
+
+type_init(wm8994_register_types)
--- /dev/null
+#ifndef QEMU_HW_WM8994_H
+#define QEMU_HW_WM8994_H
+
+#include "qdev.h"
+
+void wm8994_set_active(DeviceState *dev, bool active);
+
+void wm8994_data_req_set(DeviceState *dev,
+ void (*data_req)(void*, int),
+ void *opaque);
+
+int wm8994_dac_write(DeviceState *dev, void *buf, int num_bytes);
+
+#endif