summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-02-27 22:08:58 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-03-28 00:01:37 +0000
commit3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch)
treeb647717f83ad56b15dc42cfdef5d04d68cd9bd6b
parent83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff)
downloadrockbox-3ec66893e3.tar.gz
rockbox-3ec66893e3.zip
New port: FiiO M3K on bare metal
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
-rw-r--r--.gitignore1
-rw-r--r--apps/SOURCES2
-rw-r--r--apps/debug_menu.c53
-rw-r--r--apps/features.txt7
-rw-r--r--apps/gui/option_select.h2
-rw-r--r--apps/keymaps/keymap-fiiom3k.c219
-rw-r--r--apps/lang/english-us.lang6
-rw-r--r--apps/lang/english.lang6
-rw-r--r--apps/plugins/battery_bench.c6
-rw-r--r--apps/plugins/blackjack.c16
-rw-r--r--apps/plugins/brickmania.c8
-rw-r--r--apps/plugins/calculator.c10
-rw-r--r--apps/plugins/calendar.c10
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.h14
-rw-r--r--apps/plugins/chessclock.c10
-rw-r--r--apps/plugins/chip8.c8
-rw-r--r--apps/plugins/chopper.c5
-rw-r--r--apps/plugins/clix.c8
-rw-r--r--apps/plugins/cube.c10
-rw-r--r--apps/plugins/doom/i_video.c12
-rw-r--r--apps/plugins/flipit.c12
-rw-r--r--apps/plugins/fractals/fractal.h12
-rw-r--r--apps/plugins/goban/goban.h12
-rw-r--r--apps/plugins/imageviewer/imageviewer_button.h14
-rw-r--r--apps/plugins/invadrox.c7
-rw-r--r--apps/plugins/jewels.c9
-rw-r--r--apps/plugins/lib/keymaps.h8
-rw-r--r--apps/plugins/lib/pluginlib_actions.c15
-rw-r--r--apps/plugins/midi/midiplay.c8
-rw-r--r--apps/plugins/minesweeper.c11
-rw-r--r--apps/plugins/mp3_encoder.c6
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c8
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c9
-rw-r--r--apps/plugins/oscilloscope.c11
-rw-r--r--apps/plugins/pacbox/pacbox.h9
-rw-r--r--apps/plugins/pegbox.c17
-rw-r--r--apps/plugins/plugin.lds9
-rw-r--r--apps/plugins/pong.c7
-rw-r--r--apps/plugins/reversi/reversi-gui.h9
-rw-r--r--apps/plugins/rockblox.c10
-rw-r--r--apps/plugins/rockboy/rockboy.c11
-rw-r--r--apps/plugins/rockpaint.c11
-rw-r--r--apps/plugins/sliding_puzzle.c8
-rw-r--r--apps/plugins/snake.c8
-rw-r--r--apps/plugins/snake2.c9
-rw-r--r--apps/plugins/sokoban.c15
-rw-r--r--apps/plugins/solitaire.c19
-rw-r--r--apps/plugins/spacerocks.c9
-rw-r--r--apps/plugins/star.c16
-rw-r--r--apps/plugins/stopwatch.c8
-rw-r--r--apps/plugins/sudoku/sudoku.h11
-rw-r--r--apps/plugins/text_viewer/tv_button.h12
-rw-r--r--apps/plugins/vu_meter.c11
-rw-r--r--apps/plugins/wormlet.c9
-rw-r--r--apps/plugins/xobox.c9
-rw-r--r--apps/plugins/zxbox/keymaps.h8
-rw-r--r--apps/plugins/zxbox/zxbox_keyb.c9
-rw-r--r--bootloader/SOURCES7
-rw-r--r--bootloader/fiiom3k-spl.c204
-rw-r--r--bootloader/fiiom3k.c96
-rw-r--r--bootloader/x1000-spl.c230
-rw-r--r--firmware/SOURCES42
-rw-r--r--firmware/drivers/audio/ak4376.c274
-rw-r--r--firmware/drivers/axp173.c419
-rw-r--r--firmware/drivers/rtc/rtc_x1000.c104
-rw-r--r--firmware/export/ak4376.h152
-rw-r--r--firmware/export/audiohw.h2
-rw-r--r--firmware/export/axp173.h94
-rw-r--r--firmware/export/config.h7
-rw-r--r--firmware/export/config/fiiom3k.h116
-rw-r--r--firmware/export/installer.h31
-rw-r--r--firmware/powermgmt.c3
-rw-r--r--firmware/target/mips/ingenic_x1000/aic-x1000.c119
-rw-r--r--firmware/target/mips/ingenic_x1000/aic-x1000.h46
-rw-r--r--firmware/target/mips/ingenic_x1000/app.lds119
-rw-r--r--firmware/target/mips/ingenic_x1000/boot.lds5
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c258
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h82
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S265
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c215
-rw-r--r--firmware/target/mips/ingenic_x1000/dma-x1000.c91
-rw-r--r--firmware/target/mips/ingenic_x1000/dma-x1000.h69
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c81
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c88
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c503
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h54
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c195
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c192
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c53
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h42
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c97
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.c84
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.h110
-rw-r--r--firmware/target/mips/ingenic_x1000/i2c-x1000.c478
-rw-r--r--firmware/target/mips/ingenic_x1000/i2c-x1000.h44
-rw-r--r--firmware/target/mips/ingenic_x1000/irq-x1000.h115
-rw-r--r--firmware/target/mips/ingenic_x1000/kernel-x1000.c74
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.c477
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h110
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.c904
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.h186
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c500
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h213
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c165
-rw-r--r--firmware/target/mips/ingenic_x1000/pwm-x1000.c170
-rw-r--r--firmware/target/mips/ingenic_x1000/pwm-x1000.h47
-rw-r--r--firmware/target/mips/ingenic_x1000/sd-x1000.c236
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.c298
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.h105
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds47
-rw-r--r--firmware/target/mips/ingenic_x1000/system-target.h148
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c418
-rw-r--r--firmware/target/mips/ingenic_x1000/timer-x1000.c85
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/aic.h359
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/cpm.h896
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ddrc.h149
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ddrc_apb.h41
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ddrphy.h155
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/dma.h112
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/dma_chn.h253
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/gpio.h196
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/i2c.h625
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/intc.h57
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/lcd.h446
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/macro.h356
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/msc.h824
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ost.h141
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/rtc.h221
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/sfc.h481
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/tcu.h192
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/wdt.h74
-rw-r--r--firmware/target/mips/mipsr2-endian.h44
-rw-r--r--firmware/target/mips/mmu-mips.c4
-rw-r--r--tools/Makefile8
-rwxr-xr-xtools/configure50
-rw-r--r--tools/mkspl-x1000.c248
-rw-r--r--tools/scramble.c2
-rwxr-xr-xutils/fiio_m3k_tools/nand_patcher.py69
-rw-r--r--utils/reggen-ng/x1000.reggen364
143 files changed, 16585 insertions, 24 deletions
diff --git a/.gitignore b/.gitignore
index 35cde028b5..6e22d4dcf8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,6 +118,7 @@ __pycache__
/tools/mknkboot
/tools/mktccboot
/tools/mkzenboot
+/tools/mkspl-x1000
/tools/iaudio_bl_flash.c
/tools/iaudio_bl_flash.h
/tools/.vagrant
diff --git a/apps/SOURCES b/apps/SOURCES
index 62291bb992..80133ebd27 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -302,6 +302,8 @@ keymaps/keymap-xduoox3ii.c
keymaps/keymap-xduoox20.c
#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
keymaps/keymap-fiiom3klinux.c
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+keymaps/keymap-fiiom3k.c
#elif CONFIG_KEYPAD == EROSQ_PAD
keymaps/keymap-erosq.c
#endif
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 9bbbfaec99..f7e179b040 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -126,6 +126,10 @@
#include "bootdata.h"
#endif
+#ifdef FIIO_M3K
+#include "installer.h"
+#endif
+
static const char* threads_getname(int selected_item, void *data,
char *buffer, size_t buffer_len)
{
@@ -2491,6 +2495,52 @@ static bool dbg_boot_data(void)
}
#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */
+#ifdef FIIO_M3K
+/* Note: this is temporary and should NOT be merged, ensure it is removed */
+static int fiio_debug_menu_action_callback(int action, struct gui_synclist *lists)
+{
+ if(action == ACTION_REDRAW) {
+ simplelist_set_line_count(0);
+ simplelist_addline("Back to menu");
+ simplelist_addline("Install bootloader");
+ simplelist_addline("Dump bootloader");
+ action = ACTION_REDRAW;
+ }
+
+ if(action == ACTION_STD_OK) {
+ int sel = gui_synclist_get_sel_pos(lists);
+ int rc = 0;
+ switch(sel) {
+ case 1:
+ rc = install_bootloader("/boot.install");
+ break;
+ case 2:
+ rc = dump_bootloader("/boot.dump");
+ break;
+ default:
+ break;
+ }
+
+ if(sel == 1 || sel == 2) {
+ const char* msg = installer_strerror(rc);
+ splashf(3*HZ, "(%d) %s", rc, msg);
+ }
+
+ action = ACTION_STD_CANCEL;
+ }
+
+ return action;
+}
+
+static bool dbg_fiio_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "FiiO debug menu", 3, NULL);
+ info.action_callback = fiio_debug_menu_action_callback;
+ return simplelist_show_list(&info);
+}
+#endif
+
/****** The menu *********/
static const struct {
unsigned char *desc; /* string or ID */
@@ -2598,6 +2648,9 @@ static const struct {
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
{"Boot data", dbg_boot_data },
#endif
+#ifdef FIIO_M3K
+ {"FiiO debug menu", dbg_fiio_menu},
+#endif
};
static int menu_action_callback(int btn, struct gui_synclist *lists)
diff --git a/apps/features.txt b/apps/features.txt
index 282f5e6a6d..4e7f986057 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -182,10 +182,15 @@ depth_3d
#endif
/* This should be AUDIOHW_HAVE_FILTER_ROLL_OFF but that is only defined later */
-#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC)
+#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_AK4376)
filter_roll_off
#endif
+/* This should be AUDIOHW_HAVE_POWER_MODE but that is not defined yet */
+#if defined(HAVE_AK4376)
+dac_power_mode
+#endif
+
#if defined(HAVE_ES9018)
es9018
#endif
diff --git a/apps/gui/option_select.h b/apps/gui/option_select.h
index 7ca9a4ebbb..476e7b81bd 100644
--- a/apps/gui/option_select.h
+++ b/apps/gui/option_select.h
@@ -25,7 +25,7 @@
#include "screen_access.h"
#include "settings.h"
-#if defined (HAVE_SCROLLWHEEL)
+#if defined (HAVE_SCROLLWHEEL) && !defined(FIIO_M3K)
/* Define this if your target makes sense to have
smaller values at the top of the list increasing down the list */
#define ASCENDING_INT_SETTINGS
diff --git a/apps/keymaps/keymap-fiiom3k.c b/apps/keymaps/keymap-fiiom3k.c
new file mode 100644
index 0000000000..e562443227
--- /dev/null
+++ b/apps/keymaps/keymap-fiiom3k.c
@@ -0,0 +1,219 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Button Code Definitions for FiiO M3K target */
+
+#include "config.h"
+#include "action.h"
+#include "button.h"
+#include "settings.h"
+
+/* {Action Code, Button code, Prereq button code } */
+
+static const struct button_mapping button_context_standard[] = {
+ {ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_PREV, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
+ {ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {ACTION_STD_MENU, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
+ {ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ {ACTION_STD_HOTKEY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ LAST_ITEM_IN_LIST
+}; /* button_context_standard */
+
+static const struct button_mapping button_context_wps[] = {
+ {ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ {ACTION_WPS_PLAY, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
+ {ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
+ {ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT},
+ {ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT},
+ {ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT},
+ {ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
+ {ACTION_WPS_MENU, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
+ {ACTION_WPS_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ {ACTION_WPS_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
+ {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_WPS_ABSETA_PREVDIR, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP},
+ {ACTION_WPS_ABSETB_NEXTDIR, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN},
+ {ACTION_WPS_ABRESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ LAST_ITEM_IN_LIST
+}; /* button_context_wps */
+
+static const struct button_mapping button_context_tree[] = {
+ {ACTION_TREE_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
+ {ACTION_TREE_WPS, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
+}; /* button_context_tree */
+
+static const struct button_mapping button_context_list[] = {
+ {ACTION_LISTTREE_PGUP, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_LISTTREE_PGUP, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_LISTTREE_PGDOWN, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_LISTTREE_PGDOWN, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_LIST_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_list */
+
+static const struct button_mapping button_context_settings[] = {
+ {ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INC, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_settings */
+
+static const struct button_mapping button_context_settings_eq[] = {
+ {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_settings_eq */
+
+static const struct button_mapping button_context_quickscreen[] = {
+ {ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE},
+ {ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_quickscreen */
+
+static const struct button_mapping button_context_pitchscreen[] = {
+ {ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE},
+ {ACTION_PS_INC_SMALL, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_PS_INC_BIG, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_PS_DEC_SMALL, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_PS_DEC_BIG, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE},
+ {ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE},
+ {ACTION_PS_TOGGLE_MODE, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {ACTION_PS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE},
+ {ACTION_PS_FASTER, BUTTON_BACK, BUTTON_NONE},
+ {ACTION_PS_SLOWER, BUTTON_MENU, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_pitchscreen */
+
+static const struct button_mapping button_context_yesnoscreen[] = {
+ {ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE},
+ {ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_yesnoscreen */
+
+static const struct button_mapping button_context_keyboard[] = {
+ {ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE},
+ {ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_KBD_BACKSPACE, BUTTON_BACK, BUTTON_NONE},
+ {ACTION_KBD_BACKSPACE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_DONE, BUTTON_PLAY, BUTTON_NONE},
+ {ACTION_KBD_ABORT, BUTTON_POWER, BUTTON_NONE},
+ {ACTION_KBD_PAGE_FLIP, BUTTON_MENU, BUTTON_NONE},
+ {ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_keyboard */
+
+const struct button_mapping* get_context_mapping(int context)
+{
+ switch (context)
+ {
+ default:
+ case CONTEXT_STD:
+ return button_context_standard;
+ case CONTEXT_WPS:
+ return button_context_wps;
+ case CONTEXT_TREE:
+ case CONTEXT_MAINMENU:
+ case CONTEXT_BOOKMARKSCREEN:
+ return button_context_tree;
+ case CONTEXT_LIST:
+ return button_context_list;
+ case CONTEXT_SETTINGS:
+ case CONTEXT_SETTINGS_TIME:
+ case CONTEXT_SETTINGS_RECTRIGGER:
+ return button_context_settings;
+ case CONTEXT_SETTINGS_EQ:
+ case CONTEXT_SETTINGS_COLOURCHOOSER:
+ return button_context_settings_eq;
+ case CONTEXT_QUICKSCREEN:
+ return button_context_quickscreen;
+ case CONTEXT_PITCHSCREEN:
+ return button_context_pitchscreen;
+ case CONTEXT_YESNOSCREEN:
+ return button_context_yesnoscreen;
+ case CONTEXT_KEYBOARD:
+ return button_context_keyboard;
+ }
+}
diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang
index d64a6c8777..5a1c4cdf8c 100644
--- a/apps/lang/english-us.lang
+++ b/apps/lang/english-us.lang
@@ -9611,15 +9611,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 52d943f75e..327584c4f6 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -9702,15 +9702,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c
index dc22489b15..f8269f5c3b 100644
--- a/apps/plugins/battery_bench.c
+++ b/apps/plugins/battery_bench.c
@@ -242,6 +242,12 @@
#define BATTERY_OFF_TXT "Power"
#define BATTERY_ON_TXT "Menu - start"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define BATTERY_ON BUTTON_PLAY
+#define BATTERY_OFF BUTTON_POWER
+#define BATTERY_ON_TXT "Play"
+#define BATTERY_OFF_TXT "Power"
+
#else
#error "No keymap defined!"
#endif
diff --git a/apps/plugins/blackjack.c b/apps/plugins/blackjack.c
index f434784a5f..0667c31ef6 100644
--- a/apps/plugins/blackjack.c
+++ b/apps/plugins/blackjack.c
@@ -607,6 +607,22 @@ enum {
#define BJACK_RIGHT BUTTON_SCROLL_FWD
#define BJACK_LEFT BUTTON_SCROLL_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define BJACK_SELECT_NAME "SELECT"
+#define BJACK_STAY_NAME "PLAY"
+#define BJACK_QUIT_NAME "POWER"
+#define BJACK_DOUBLE_NAME "MENU"
+#define BJACK_SELECT BUTTON_SELECT
+#define BJACK_QUIT BUTTON_POWER
+#define BJACK_MAX BUTTON_VOL_UP
+#define BJACK_MIN BUTTON_VOL_DOWN
+#define BJACK_STAY BUTTON_PLAY
+#define BJACK_DOUBLEDOWN BUTTON_MENU
+#define BJACK_UP BUTTON_UP
+#define BJACK_DOWN BUTTON_DOWN
+#define BJACK_RIGHT BUTTON_RIGHT
+#define BJACK_LEFT BUTTON_LEFT
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index 70ad58a908..92f8d4d161 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -342,6 +342,14 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_PREV
#define DOWN BUTTON_NEXT
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define SELECT BUTTON_PLAY
+#define UP BUTTON_UP
+#define DOWN BUTTON_DOWN
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/calculator.c b/apps/plugins/calculator.c
index 46d9da6149..4d1b6fa594 100644
--- a/apps/plugins/calculator.c
+++ b/apps/plugins/calculator.c
@@ -536,6 +536,16 @@ F3: equal to "="
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CALCULATOR_LEFT BUTTON_LEFT
+#define CALCULATOR_RIGHT BUTTON_RIGHT
+#define CALCULATOR_UP BUTTON_UP
+#define CALCULATOR_DOWN BUTTON_DOWN
+#define CALCULATOR_QUIT BUTTON_POWER
+#define CALCULATOR_INPUT BUTTON_PLAY
+#define CALCULATOR_CALC BUTTON_MENU
+#define CALCULATOR_CLEAR BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/calendar.c b/apps/plugins/calendar.c
index b52a62328f..54173fde1c 100644
--- a/apps/plugins/calendar.c
+++ b/apps/plugins/calendar.c
@@ -421,6 +421,16 @@
#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CALENDAR_QUIT BUTTON_POWER
+#define CALENDAR_SELECT BUTTON_SELECT
+#define CALENDAR_NEXT_WEEK BUTTON_DOWN
+#define CALENDAR_PREV_WEEK BUTTON_UP
+#define CALENDAR_NEXT_DAY BUTTON_LEFT
+#define CALENDAR_PREV_DAY BUTTON_RIGHT
+#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
+#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
+
#else
#error "No keypad setting."
#endif
diff --git a/apps/plugins/chessbox/chessbox_pgn.h b/apps/plugins/chessbox/chessbox_pgn.h
index aacc2ba412..5bfce10ec5 100644
--- a/apps/plugins/chessbox/chessbox_pgn.h
+++ b/apps/plugins/chessbox/chessbox_pgn.h
@@ -581,6 +581,20 @@
#define CB_SCROLL_LEFT (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
#define CB_SCROLL_RIGHT (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CB_SELECT BUTTON_SELECT
+#define CB_UP BUTTON_UP
+#define CB_DOWN BUTTON_DOWN
+#define CB_LEFT BUTTON_LEFT
+#define CB_RIGHT BUTTON_RIGHT
+#define CB_PLAY BUTTON_PLAY
+#define CB_MENU BUTTON_MENU
+#define CB_LEVEL BUTTON_BACK
+#define CB_SCROLL_UP (BUTTON_UP|BUTTON_REPEAT)
+#define CB_SCROLL_DOWN (BUTTON_DOWN|BUTTON_REPEAT)
+#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
+#define CB_SCROLL_RIGHT (BUTTON_RIGHT|BUTTON_REPEAT)
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chessclock.c b/apps/plugins/chessclock.c
index 9b933cdcd4..9a30dee984 100644
--- a/apps/plugins/chessclock.c
+++ b/apps/plugins/chessclock.c
@@ -406,6 +406,16 @@
#define CHC_SETTINGS_OK BUTTON_PLAY
#define CHC_SETTINGS_CANCEL BUTTON_POWER
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CHC_QUIT BUTTON_POWER
+#define CHC_STARTSTOP BUTTON_PLAY
+#define CHC_RESET BUTTON_BACK
+#define CHC_MENU BUTTON_MENU
+#define CHC_SETTINGS_INC BUTTON_UP
+#define CHC_SETTINGS_DEC BUTTON_DOWN
+#define CHC_SETTINGS_OK BUTTON_SELECT
+#define CHC_SETTINGS_CANCEL BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chip8.c b/apps/plugins/chip8.c
index dde6faaccf..31866acd10 100644
--- a/apps/plugins/chip8.c
+++ b/apps/plugins/chip8.c
@@ -1283,6 +1283,14 @@ CONFIG_KEYPAD == MROBE500_PAD
#define CHIP8_KEY6 BUTTON_BACK
#define CHIP8_KEY8 BUTTON_PREV
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CHIP8_OFF BUTTON_POWER
+#define CHIP8_KEY2 BUTTON_MENU
+#define CHIP8_KEY4 BUTTON_LEFT
+#define CHIP8_KEY5 BUTTON_SELECT
+#define CHIP8_KEY6 BUTTON_RIGHT
+#define CHIP8_KEY8 BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chopper.c b/apps/plugins/chopper.c
index 23b32150da..ab8f7b5540 100644
--- a/apps/plugins/chopper.c
+++ b/apps/plugins/chopper.c
@@ -203,6 +203,11 @@ CONFIG_KEYPAD == MROBE500_PAD
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define QUIT BUTTON_POWER
+#define ACTION BUTTON_PLAY
+#define ACTIONTEXT "PLAY"
+
#elif !defined(HAVE_TOUCHSCREEN)
#error No keymap defined!
#endif
diff --git a/apps/plugins/clix.c b/apps/plugins/clix.c
index eb0e0d1a59..f1f21f6312 100644
--- a/apps/plugins/clix.c
+++ b/apps/plugins/clix.c
@@ -316,6 +316,14 @@
#define CLIX_BUTTON_RIGHT BUTTON_SCROLL_FWD
#define CLIX_BUTTON_CLICK BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CLIX_BUTTON_QUIT BUTTON_POWER
+#define CLIX_BUTTON_UP BUTTON_UP
+#define CLIX_BUTTON_DOWN BUTTON_DOWN
+#define CLIX_BUTTON_LEFT BUTTON_LEFT
+#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
+#define CLIX_BUTTON_CLICK BUTTON_SELECT
+
#else
#error "no keymap"
#endif
diff --git a/apps/plugins/cube.c b/apps/plugins/cube.c
index b84a2636cb..6587fdcb65 100644
--- a/apps/plugins/cube.c
+++ b/apps/plugins/cube.c
@@ -410,6 +410,16 @@
#define CUBE_PAUSE BUTTON_BACK
#define CUBE_HIGHSPEED BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define CUBE_QUIT BUTTON_POWER
+#define CUBE_NEXT BUTTON_RIGHT
+#define CUBE_PREV BUTTON_LEFT
+#define CUBE_INC BUTTON_SCROLL_FWD
+#define CUBE_DEC BUTTON_SCROLL_BACK
+#define CUBE_MODE BUTTON_MENU
+#define CUBE_PAUSE BUTTON_PLAY
+#define CUBE_HIGHSPEED BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/doom/i_video.c b/apps/plugins/doom/i_video.c
index 233c1cac8f..bb04194acc 100644
--- a/apps/plugins/doom/i_video.c
+++ b/apps/plugins/doom/i_video.c
@@ -612,6 +612,18 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define DOOMBUTTON_UP BUTTON_UP
+#define DOOMBUTTON_DOWN BUTTON_DOWN
+#define DOOMBUTTON_LEFT BUTTON_LEFT
+#define DOOMBUTTON_RIGHT BUTTON_RIGHT
+#define DOOMBUTTON_SHOOT BUTTON_SELECT
+#define DOOMBUTTON_OPEN BUTTON_PLAY
+#define DOOMBUTTON_ESC BUTTON_POWER
+#define DOOMBUTTON_ENTER BUTTON_SELECT
+#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
+#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
+
#else
#error Keymap not defined!
#endif
diff --git a/apps/plugins/flipit.c b/apps/plugins/flipit.c
index 4a6e64a5be..f6fb059f91 100644
--- a/apps/plugins/flipit.c
+++ b/apps/plugins/flipit.c
@@ -484,6 +484,18 @@
#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
#define FLIPIT_TOGGLE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+
+#define FLIPIT_LEFT BUTTON_LEFT
+#define FLIPIT_RIGHT BUTTON_RIGHT
+#define FLIPIT_UP BUTTON_UP
+#define FLIPIT_DOWN BUTTON_DOWN
+#define FLIPIT_QUIT BUTTON_POWER
+#define FLIPIT_SHUFFLE BUTTON_PLAY
+#define FLIPIT_SOLVE BUTTON_VOL_DOWN
+#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
+#define FLIPIT_TOGGLE BUTTON_SELECT
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/fractals/fractal.h b/apps/plugins/fractals/fractal.h
index 55bd513fe0..28172aed9f 100644
--- a/apps/plugins/fractals/fractal.h
+++ b/apps/plugins/fractals/fractal.h
@@ -506,6 +506,18 @@
#define FRACTAL_PRECISION_DEC BUTTON_BACK
#define FRACTAL_RESET BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define FRACTAL_QUIT BUTTON_POWER
+#define FRACTAL_UP BUTTON_UP
+#define FRACTAL_DOWN BUTTON_DOWN
+#define FRACTAL_LEFT BUTTON_LEFT
+#define FRACTAL_RIGHT BUTTON_RIGHT
+#define FRACTAL_ZOOM_IN BUTTON_VOL_UP
+#define FRACTAL_ZOOM_OUT BUTTON_VOL_DOWN
+#define FRACTAL_PRECISION_INC BUTTON_MENU
+#define FRACTAL_PRECISION_DEC BUTTON_BACK
+#define FRACTAL_RESET BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/goban/goban.h b/apps/plugins/goban/goban.h
index 375588c7d8..9f1a200cc2 100644
--- a/apps/plugins/goban/goban.h
+++ b/apps/plugins/goban/goban.h
@@ -492,6 +492,18 @@
#define GBN_BUTTON_CONTEXT BUTTON_MENU | BUTTON_REPEAT
#define GBN_BUTTON_NEXT_VAR BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define GBN_BUTTON_UP BUTTON_UP
+#define GBN_BUTTON_DOWN BUTTON_DOWN
+#define GBN_BUTTON_LEFT BUTTON_LEFT
+#define GBN_BUTTON_RIGHT BUTTON_RIGHT
+#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
+#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
+#define GBN_BUTTON_MENU BUTTON_MENU
+#define GBN_BUTTON_PLAY BUTTON_PLAY
+#define GBN_BUTTON_CONTEXT (BUTTON_SELECT|BUTTON_REPEAT)
+#define GBN_BUTTON_NEXT_VAR BUTTON_BACK
+
#else
#error Unsupported keypad
#endif
diff --git a/apps/plugins/imageviewer/imageviewer_button.h b/apps/plugins/imageviewer/imageviewer_button.h
index 221ea3380e..cfb9a2969b 100644
--- a/apps/plugins/imageviewer/imageviewer_button.h
+++ b/apps/plugins/imageviewer/imageviewer_button.h
@@ -536,6 +536,20 @@
#define IMGVIEW_MENU BUTTON_MENU
#define IMGVIEW_SLIDE_SHOW BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define IMGVIEW_ZOOM_IN BUTTON_VOL_UP
+#define IMGVIEW_ZOOM_OUT BUTTON_VOL_DOWN
+#define IMGVIEW_UP BUTTON_UP
+#define IMGVIEW_DOWN BUTTON_DOWN
+#define IMGVIEW_LEFT BUTTON_LEFT
+#define IMGVIEW_RIGHT BUTTON_RIGHT
+#define IMGVIEW_NEXT BUTTON_BACK
+#define IMGVIEW_NEXT_REPEAT (BUTTON_BACK|BUTTON_REPEAT)
+#define IMGVIEW_PREVIOUS BUTTON_MENU
+#define IMGVIEW_PREVIOUS_REPEAT (BUTTON_MENU|BUTTON_REPEAT)
+#define IMGVIEW_MENU BUTTON_POWER
+#define IMGVIEW_SLIDE_SHOW BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/invadrox.c b/apps/plugins/invadrox.c
index 21bba6d9d4..8b0519d9d0 100644
--- a/apps/plugins/invadrox.c
+++ b/apps/plugins/invadrox.c
@@ -296,6 +296,13 @@ CONFIG_KEYPAD == MROBE500_PAD
#define RIGHT BUTTON_SCROLL_FWD
#define FIRE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define FIRE BUTTON_SELECT
+
#else
#error INVADROX: Unsupported keypad
#endif
diff --git a/apps/plugins/jewels.c b/apps/plugins/jewels.c
index 928cd78c95..83a44eea02 100644
--- a/apps/plugins/jewels.c
+++ b/apps/plugins/jewels.c
@@ -387,6 +387,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define HK_SELECT "PLAY"
#define HK_CANCEL "BACK"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define JEWELS_UP BUTTON_UP
+#define JEWELS_DOWN BUTTON_DOWN
+#define JEWELS_LEFT BUTTON_LEFT
+#define JEWELS_RIGHT BUTTON_RIGHT
+#define JEWELS_SELECT BUTTON_SELECT
+#define JEWELS_CANCEL BUTTON_BACK
+#define HK_SELECT "SELECT"
+#define HK_CANCEL "BACK"
#else
#error No keymap defined!
diff --git a/apps/plugins/lib/keymaps.h b/apps/plugins/lib/keymaps.h
index b0667b13cc..b660d4d85e 100644
--- a/apps/plugins/lib/keymaps.h
+++ b/apps/plugins/lib/keymaps.h
@@ -247,6 +247,14 @@
#define BTN_FIRE BUTTON_PLAY
#define BTN_PAUSE BUTTON_POWER
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define BTN_UP BUTTON_UP
+#define BTN_DOWN BUTTON_DOWN
+#define BTN_LEFT BUTTON_LEFT
+#define BTN_RIGHT BUTTON_RIGHT
+#define BTN_FIRE BUTTON_SELECT
+#define BTN_PAUSE BUTTON_POWER
+
#else
#error Unsupported keypad
#endif
diff --git a/apps/plugins/lib/pluginlib_actions.c b/apps/plugins/lib/pluginlib_actions.c
index 4b5a0a4505..b1bcd06ee6 100644
--- a/apps/plugins/lib/pluginlib_actions.c
+++ b/apps/plugins/lib/pluginlib_actions.c
@@ -269,6 +269,15 @@ const struct button_mapping pla_main_ctx[] =
{ PLA_DOWN_REPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_LEFT_REPEAT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_RIGHT_REPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+#elif (CONFIG_KEYPAD == FIIO_M3K_PAD)
+ { PLA_UP, BUTTON_UP, BUTTON_NONE },
+ { PLA_DOWN, BUTTON_DOWN, BUTTON_NONE },
+ { PLA_LEFT, BUTTON_LEFT, BUTTON_NONE },
+ { PLA_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
+ { PLA_UP_REPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
+ { PLA_DOWN_REPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+ { PLA_LEFT_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ { PLA_RIGHT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No directions defined
@@ -506,6 +515,12 @@ const struct button_mapping pla_main_ctx[] =
{PLA_SELECT, BUTTON_PLAY, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
{PLA_SELECT_REPEAT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE},
+#elif (CONFIG_KEYPAD == FIIO_M3K_PAD)
+ {PLA_CANCEL, BUTTON_BACK, BUTTON_NONE},
+ {PLA_EXIT, BUTTON_POWER, BUTTON_NONE},
+ {PLA_SELECT, BUTTON_SELECT, BUTTON_NONE},
+ {PLA_SELECT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {PLA_SELECT_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No actions defined
diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c
index 7dde947e1c..f07cdd4dcf 100644
--- a/apps/plugins/midi/midiplay.c
+++ b/apps/plugins/midi/midiplay.c
@@ -325,6 +325,14 @@
#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
#define MIDI_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MIDI_QUIT BUTTON_POWER
+#define MIDI_FFWD BUTTON_LEFT
+#define MIDI_REWIND BUTTON_RIGHT
+#define MIDI_VOL_UP BUTTON_VOL_UP
+#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
+#define MIDI_PLAYPAUSE BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
index 590ea56d08..19a6b99f12 100644
--- a/apps/plugins/minesweeper.c
+++ b/apps/plugins/minesweeper.c
@@ -432,6 +432,17 @@ CONFIG_KEYPAD == MROBE500_PAD
# define MINESWP_DISCOVER (BUTTON_PLAY | BUTTON_REPEAT)
# define MINESWP_INFO BUTTON_VOL_UP
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+# define MINESWP_LEFT BUTTON_LEFT
+# define MINESWP_RIGHT BUTTON_RIGHT
+# define MINESWP_UP BUTTON_UP
+# define MINESWP_DOWN BUTTON_DOWN
+# define MINESWP_QUIT BUTTON_POWER
+# define MINESWP_TOGGLE_PRE BUTTON_SELECT
+# define MINESWP_TOGGLE (BUTTON_SELECT|BUTTON_REL)
+# define MINESWP_DISCOVER (BUTTON_SELECT|BUTTON_REPEAT)
+# define MINESWP_INFO BUTTON_MENU
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mp3_encoder.c b/apps/plugins/mp3_encoder.c
index 244265b250..7c5084529b 100644
--- a/apps/plugins/mp3_encoder.c
+++ b/apps/plugins/mp3_encoder.c
@@ -2580,6 +2580,12 @@ CONFIG_KEYPAD == MROBE500_PAD
#define MP3ENC_DONE BUTTON_POWER
#define MP3ENC_SELECT BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MP3ENC_PREV BUTTON_LEFT
+#define MP3ENC_NEXT BUTTON_RIGHT
+#define MP3ENC_DONE BUTTON_POWER
+#define MP3ENC_SELECT BUTTON_SELECT
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 44c72dd853..d59797eaa7 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -354,6 +354,14 @@ struct mpeg_settings settings;
#define MPEG_START_TIME_DOWN BUTTON_NEXT
#define MPEG_START_TIME_EXIT BUTTON_POWER
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 81b6d37857..6b18e4fa3a 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -486,6 +486,15 @@ CONFIG_KEYPAD == SANSA_M200_PAD
#define MPEG_RW BUTTON_PREV
#define MPEG_FF BUTTON_NEXT
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index d50de7f2a0..881295d6ab 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -537,6 +537,17 @@
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
+#define OSCILLOSCOPE_ADVMODE BUTTON_PLAY
+#define OSCILLOSCOPE_ORIENTATION BUTTON_BACK
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_SCROLL_BACK
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_SCROLL_FWD
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/pacbox/pacbox.h b/apps/plugins/pacbox/pacbox.h
index 48196146a7..c1a1c6b9e5 100644
--- a/apps/plugins/pacbox/pacbox.h
+++ b/apps/plugins/pacbox/pacbox.h
@@ -401,6 +401,15 @@
#define PACMAN_1UP BUTTON_VOL_UP
#define PACMAN_COIN BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define PACMAN_UP BUTTON_UP
+#define PACMAN_DOWN BUTTON_DOWN
+#define PACMAN_LEFT BUTTON_LEFT
+#define PACMAN_RIGHT BUTTON_RIGHT
+#define PACMAN_MENU BUTTON_MENU
+#define PACMAN_1UP BUTTON_VOL_UP
+#define PACMAN_COIN BUTTON_PLAY
+
#else
#error Keymap not defined!
diff --git a/apps/plugins/pegbox.c b/apps/plugins/pegbox.c
index e16cf2921e..498aa36357 100644
--- a/apps/plugins/pegbox.c
+++ b/apps/plugins/pegbox.c
@@ -711,6 +711,23 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LVL_UP_TEXT "VOL+"
#define LVL_DOWN_TEXT "VOL-"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define PEGBOX_SELECT BUTTON_SELECT
+#define PEGBOX_QUIT BUTTON_POWER
+#define PEGBOX_RESTART BUTTON_BACK
+#define PEGBOX_LVL_UP BUTTON_VOL_UP
+#define PEGBOX_LVL_DOWN BUTTON_VOL_DOWN
+#define PEGBOX_UP BUTTON_UP
+#define PEGBOX_DOWN BUTTON_DOWN
+#define PEGBOX_RIGHT BUTTON_RIGHT
+#define PEGBOX_LEFT BUTTON_LEFT
+
+#define SELECT_TEXT "SELECT"
+#define QUIT_TEXT "POWER"
+#define RESTART_TEXT "BACK"
+#define LVL_UP_TEXT "VOL+"
+#define LVL_DOWN_TEXT "VOL-"
+
#else
#error "Unsupported keymap!"
#endif
diff --git a/apps/plugins/plugin.lds b/apps/plugins/plugin.lds
index 25ff4a461b..5931b6b0a5 100644
--- a/apps/plugins/plugin.lds
+++ b/apps/plugins/plugin.lds
@@ -76,6 +76,10 @@ OUTPUT_FORMAT(elf32-littlemips)
#elif CONFIG_CPU==IMX233
#include "cpu.h"
#define DRAMSIZE (DRAM_SIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - FRAME_SIZE - TTB_SIZE)
+#elif CONFIG_CPU==X1000
+#include "config.h"
+#undef STUBOFFSET
+#define STUBOFFSET 0x4000
#endif
/* default to full RAM (minus codecs&plugins) unless specified otherwise */
@@ -174,6 +178,11 @@ OUTPUT_FORMAT(elf32-littlemips)
#define IRAMSIZE 0
/* The bit of IRAM that is available is used in the core */
+#elif CONFIG_CPU == X1000
+#define DRAMORIG (0x80000000 + STUBOFFSET)
+#define IRAM DRAM
+#define IRAMSIZE 0
+
#elif CONFIG_CPU == RK27XX
#define DRAMORIG 0x60000000
#define IRAM DRAM
diff --git a/apps/plugins/pong.c b/apps/plugins/pong.c
index 4df4db66bb..b06789af6b 100644
--- a/apps/plugins/pong.c
+++ b/apps/plugins/pong.c
@@ -325,6 +325,13 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PONG_RIGHT_UP BUTTON_PREV
#define PONG_RIGHT_DOWN BUTTON_NEXT
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define PONG_QUIT BUTTON_POWER
+#define PONG_PAUSE BUTTON_PLAY
+#define PONG_LEFT_UP BUTTON_MENU
+#define PONG_LEFT_DOWN BUTTON_LEFT
+#define PONG_RIGHT_UP BUTTON_BACK
+#define PONG_RIGHT_DOWN BUTTON_RIGHT
#else
#error No keymap defined!
diff --git a/apps/plugins/reversi/reversi-gui.h b/apps/plugins/reversi/reversi-gui.h
index c94ca59179..fd60b23927 100644
--- a/apps/plugins/reversi/reversi-gui.h
+++ b/apps/plugins/reversi/reversi-gui.h
@@ -361,6 +361,15 @@
#define REVERSI_BUTTON_MAKE_MOVE BUTTON_PLAY
#define REVERSI_BUTTON_MENU BUTTON_MENU
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define REVERSI_BUTTON_QUIT BUTTON_POWER
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU BUTTON_MENU
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/rockblox.c b/apps/plugins/rockblox.c
index 7958818a8e..9c36f8f62f 100644
--- a/apps/plugins/rockblox.c
+++ b/apps/plugins/rockblox.c
@@ -477,6 +477,16 @@
#define ROCKBLOX_DROP BUTTON_PLAY
#define ROCKBLOX_RESTART BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define ROCKBLOX_OFF BUTTON_POWER
+#define ROCKBLOX_ROTATE_CCW BUTTON_UP
+#define ROCKBLOX_ROTATE_CW BUTTON_DOWN
+#define ROCKBLOX_DOWN BUTTON_SELECT
+#define ROCKBLOX_LEFT BUTTON_LEFT
+#define ROCKBLOX_RIGHT BUTTON_RIGHT
+#define ROCKBLOX_DROP BUTTON_PLAY
+#define ROCKBLOX_RESTART BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/rockboy/rockboy.c b/apps/plugins/rockboy/rockboy.c
index 1f65b780a6..6dd010961e 100644
--- a/apps/plugins/rockboy/rockboy.c
+++ b/apps/plugins/rockboy/rockboy.c
@@ -467,6 +467,17 @@ static void setoptions (void)
options.SELECT = BUTTON_VOL_UP;
options.MENU = BUTTON_POWER;
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+ options.UP = BUTTON_UP;
+ options.DOWN = BUTTON_DOWN;
+ options.LEFT = BUTTON_LEFT;
+ options.RIGHT = BUTTON_RIGHT;
+ options.A = BUTTON_MENU;
+ options.B = BUTTON_BACK;
+ options.START = BUTTON_VOL_DOWN;
+ options.SELECT = BUTTON_VOL_UP;
+ options.MENU = BUTTON_POWER;
+
#else
#error No Keymap Defined!
#endif
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index e2b1f2a3e3..0d3211d4d2 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -393,6 +393,17 @@
#define ROCKPAINT_LEFT BUTTON_HOME
#define ROCKPAINT_RIGHT BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define ROCKPAINT_QUIT BUTTON_POWER
+#define ROCKPAINT_DRAW BUTTON_SELECT
+#define ROCKPAINT_MENU BUTTON_MENU
+#define ROCKPAINT_TOOLBAR BUTTON_VOL_UP
+#define ROCKPAINT_TOOLBAR2 BUTTON_VOL_DOWN
+#define ROCKPAINT_UP BUTTON_UP
+#define ROCKPAINT_DOWN BUTTON_DOWN
+#define ROCKPAINT_LEFT BUTTON_LEFT
+#define ROCKPAINT_RIGHT BUTTON_RIGHT
+
#else
#error "Please define keys for this keypad"
#endif
diff --git a/apps/plugins/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c
index dec400c079..d1820b2f50 100644
--- a/apps/plugins/sliding_puzzle.c
+++ b/apps/plugins/sliding_puzzle.c
@@ -348,6 +348,14 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PUZZLE_SHUFFLE BUTTON_BACK
#define PUZZLE_PICTURE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define PUZZLE_QUIT BUTTON_POWER
+#define PUZZLE_LEFT BUTTON_LEFT
+#define PUZZLE_RIGHT BUTTON_RIGHT
+#define PUZZLE_UP BUTTON_UP
+#define PUZZLE_DOWN BUTTON_DOWN
+#define PUZZLE_SHUFFLE BUTTON_BACK
+#define PUZZLE_PICTURE BUTTON_PLAY
#else
#error No keymap defined!
diff --git a/apps/plugins/snake.c b/apps/plugins/snake.c
index c5c28eba6e..459d345fa2 100644
--- a/apps/plugins/snake.c
+++ b/apps/plugins/snake.c
@@ -305,6 +305,14 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#define SNAKE_DOWN BUTTON_NEXT
#define SNAKE_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define SNAKE_QUIT BUTTON_POWER
+#define SNAKE_LEFT BUTTON_LEFT
+#define SNAKE_RIGHT BUTTON_RIGHT
+#define SNAKE_UP BUTTON_UP
+#define SNAKE_DOWN BUTTON_DOWN
+#define SNAKE_PLAYPAUSE BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/snake2.c b/apps/plugins/snake2.c
index fcc1790a13..1536840daf 100644
--- a/apps/plugins/snake2.c
+++ b/apps/plugins/snake2.c
@@ -437,6 +437,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define SNAKE2_LEFT BUTTON_LEFT
+#define SNAKE2_RIGHT BUTTON_RIGHT
+#define SNAKE2_UP BUTTON_UP
+#define SNAKE2_DOWN BUTTON_DOWN
+#define SNAKE2_QUIT BUTTON_POWER
+#define SNAKE2_PLAYPAUSE BUTTON_PLAY
+#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 2dcea7546f..9d57d7428c 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -695,6 +695,21 @@
#define BUTTON_SAVE BUTTON_PLAY
#define BUTTON_SAVE_NAME "PLAY"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define SOKOBAN_LEFT BUTTON_LEFT
+#define SOKOBAN_RIGHT BUTTON_RIGHT
+#define SOKOBAN_UP BUTTON_UP
+#define SOKOBAN_DOWN BUTTON_DOWN
+#define SOKOBAN_MENU BUTTON_MENU
+#define SOKOBAN_UNDO BUTTON_VOL_DOWN
+#define SOKOBAN_REDO BUTTON_VOL_UP
+#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY|BUTTON_VOL_DOWN)
+#define SOKOBAN_LEVEL_UP (BUTTON_PLAY|BUTTON_VOL_UP)
+#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY|BUTTON_POWER)
+#define SOKOBAN_PAUSE BUTTON_SELECT
+#define BUTTON_SAVE BUTTON_BACK
+#define BUTTON_SAVE_NAME "BACK"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
index d14c608396..2d737df678 100644
--- a/apps/plugins/solitaire.c
+++ b/apps/plugins/solitaire.c
@@ -722,6 +722,25 @@ CONFIG_KEYPAD == MROBE500_PAD
# define HK_CUR2STACK "DBL PLAY"
# define HK_REM2STACK "NEXT"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+# define SOL_QUIT BUTTON_POWER
+# define SOL_UP BUTTON_UP
+# define SOL_DOWN BUTTON_DOWN
+# define SOL_LEFT BUTTON_LEFT
+# define SOL_RIGHT BUTTON_RIGHT
+# define SOL_MOVE_PRE BUTTON_SELECT
+# define SOL_MOVE (BUTTON_SELECT|BUTTON_REL)
+# define SOL_DRAW BUTTON_PLAY
+# define SOL_REM2CUR BUTTON_VOL_DOWN
+# define SOL_CUR2STACK_PRE BUTTON_SELECT
+# define SOL_CUR2STACK (BUTTON_SELECT|BUTTON_REPEAT)
+# define SOL_REM2STACK BUTTON_VOL_UP
+# define HK_MOVE "SELECT"
+# define HK_DRAW "PLAY"
+# define HK_REM2CUR "VOL-"
+# define HK_CUR2STACK "HOLD SELECT"
+# define HK_REM2STACK "VOL+"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/spacerocks.c b/apps/plugins/spacerocks.c
index 608f3ee512..2d39c26b3d 100644
--- a/apps/plugins/spacerocks.c
+++ b/apps/plugins/spacerocks.c
@@ -363,6 +363,15 @@
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define AST_PAUSE BUTTON_MENU
+#define AST_QUIT BUTTON_POWER
+#define AST_THRUST BUTTON_SELECT
+#define AST_HYPERSPACE BUTTON_BACK
+#define AST_LEFT BUTTON_LEFT
+#define AST_RIGHT BUTTON_RIGHT
+#define AST_FIRE BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/star.c b/apps/plugins/star.c
index 9de5032699..c186474ae3 100644
--- a/apps/plugins/star.c
+++ b/apps/plugins/star.c
@@ -652,6 +652,22 @@
#define STAR_LEVEL_DOWN_NAME "VOL DN"
#define STAR_LEVEL_REPEAT_NAME "BACK"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define STAR_QUIT BUTTON_POWER
+#define STAR_LEFT BUTTON_LEFT
+#define STAR_RIGHT BUTTON_RIGHT
+#define STAR_UP BUTTON_UP
+#define STAR_DOWN BUTTON_DOWN
+#define STAR_TOGGLE_CONTROL BUTTON_PLAY
+#define STAR_LEVEL_UP BUTTON_VOL_UP
+#define STAR_LEVEL_DOWN BUTTON_VOL_DOWN
+#define STAR_LEVEL_REPEAT BUTTON_BACK
+#define STAR_TOGGLE_CONTROL_NAME "PLAY"
+#define STAR_QUIT_NAME "POWER"
+#define STAR_LEVEL_UP_NAME "VOL+"
+#define STAR_LEVEL_DOWN_NAME "VOL-"
+#define STAR_LEVEL_REPEAT_NAME "BACK"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/stopwatch.c b/apps/plugins/stopwatch.c
index 82e6fba5c6..5259d0d616 100644
--- a/apps/plugins/stopwatch.c
+++ b/apps/plugins/stopwatch.c
@@ -309,6 +309,14 @@
#define STOPWATCH_SCROLL_UP BUTTON_SCROLL_FWD
#define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define STOPWATCH_QUIT BUTTON_POWER
+#define STOPWATCH_START_STOP BUTTON_PLAY
+#define STOPWATCH_RESET_TIMER BUTTON_BACK
+#define STOPWATCH_LAP_TIMER BUTTON_SELECT
+#define STOPWATCH_SCROLL_UP BUTTON_SCROLL_BACK
+#define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_FWD
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/sudoku/sudoku.h b/apps/plugins/sudoku/sudoku.h
index de3802d9bc..6ee3eb33ee 100644
--- a/apps/plugins/sudoku/sudoku.h
+++ b/apps/plugins/sudoku/sudoku.h
@@ -459,6 +459,17 @@
#define SUDOKU_BUTTON_MENU (BUTTON_MENU | BUTTON_REL)
#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define SUDOKU_BUTTON_QUIT BUTTON_POWER
+#define SUDOKU_BUTTON_UP BUTTON_UP
+#define SUDOKU_BUTTON_DOWN BUTTON_DOWN
+#define SUDOKU_BUTTON_LEFT BUTTON_LEFT
+#define SUDOKU_BUTTON_RIGHT BUTTON_RIGHT
+#define SUDOKU_BUTTON_TOGGLEBACK BUTTON_VOL_DOWN
+#define SUDOKU_BUTTON_TOGGLE BUTTON_VOL_UP
+#define SUDOKU_BUTTON_MENU BUTTON_MENU
+#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/text_viewer/tv_button.h b/apps/plugins/text_viewer/tv_button.h
index ba71201cda..bea8f829ef 100644
--- a/apps/plugins/text_viewer/tv_button.h
+++ b/apps/plugins/text_viewer/tv_button.h
@@ -583,6 +583,18 @@
#define TV_LINE_DOWN (BUTTON_POWER | BUTTON_VOL_DOWN)
#define TV_BOOKMARK (BUTTON_POWER | BUTTON_PLAY)
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_VOL_UP
+#define TV_SCROLL_DOWN BUTTON_VOL_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_SELECT
+#define TV_LINE_UP BUTTON_SCROLL_BACK
+#define TV_LINE_DOWN BUTTON_SCROLL_FWD
+#define TV_BOOKMARK BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c
index 629294ce7f..e24ad8dcdc 100644
--- a/apps/plugins/vu_meter.c
+++ b/apps/plugins/vu_meter.c
@@ -441,6 +441,17 @@
#define LABEL_MENU "MENU"
#define LABEL_VOLUME "VOL UP/DN"
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define VUMETER_QUIT BUTTON_POWER
+#define VUMETER_HELP BUTTON_BACK
+#define VUMETER_MENU BUTTON_MENU
+#define VUMETER_UP BUTTON_VOL_UP
+#define VUMETER_DOWN BUTTON_VOL_DOWN
+#define LABEL_HELP "BACK"
+#define LABEL_QUIT "POWER"
+#define LABEL_MENU "MENU"
+#define LABEL_VOLUME "VOL+/VOL-"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c
index d3db768535..d76f6a7d5a 100644
--- a/apps/plugins/wormlet.c
+++ b/apps/plugins/wormlet.c
@@ -398,6 +398,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define BTN_QUIT BUTTON_POWER
#define BTN_STOPRESET BUTTON_BACK
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define BTN_DIR_UP BUTTON_UP
+#define BTN_DIR_DOWN BUTTON_DOWN
+#define BTN_DIR_LEFT BUTTON_LEFT
+#define BTN_DIR_RIGHT BUTTON_RIGHT
+#define BTN_STARTPAUSE BUTTON_PLAY
+#define BTN_QUIT BUTTON_POWER
+#define BTN_STOPRESET BUTTON_BACK
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/xobox.c b/apps/plugins/xobox.c
index e04ecc225d..36aae071cb 100644
--- a/apps/plugins/xobox.c
+++ b/apps/plugins/xobox.c
@@ -351,6 +351,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define DOWN BUTTON_NEXT
#define PAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define UP BUTTON_UP
+#define DOWN BUTTON_DOWN
+#define PAUSE BUTTON_PLAY
+
#else
#error "No keymap defined!"
#endif
diff --git a/apps/plugins/zxbox/keymaps.h b/apps/plugins/zxbox/keymaps.h
index e62c33c157..dbebc3b1d9 100644
--- a/apps/plugins/zxbox/keymaps.h
+++ b/apps/plugins/zxbox/keymaps.h
@@ -298,6 +298,14 @@
#define ZX_UP BUTTON_PREV
#define ZX_DOWN BUTTON_NEXT
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define ZX_SELECT BUTTON_SELECT
+#define ZX_MENU BUTTON_MENU
+#define ZX_LEFT BUTTON_LEFT
+#define ZX_RIGHT BUTTON_RIGHT
+#define ZX_UP BUTTON_UP
+#define ZX_DOWN BUTTON_DOWN
+
#else
#error Keymap not defined!
diff --git a/apps/plugins/zxbox/zxbox_keyb.c b/apps/plugins/zxbox/zxbox_keyb.c
index 6237ded8c1..dd517e01e7 100644
--- a/apps/plugins/zxbox/zxbox_keyb.c
+++ b/apps/plugins/zxbox/zxbox_keyb.c
@@ -293,6 +293,15 @@
#define KBD_UP BUTTON_PREV
#define KBD_DOWN BUTTON_NEXT
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+
+#define KBD_SELECT BUTTON_SELECT
+#define KBD_ABORT BUTTON_BACK
+#define KBD_LEFT BUTTON_LEFT
+#define KBD_RIGHT BUTTON_RIGHT
+#define KBD_UP BUTTON_UP
+#define KBD_DOWN BUTTON_DOWN
+
#endif
#ifdef HAVE_TOUCHSCREEN
diff --git a/bootloader/SOURCES b/bootloader/SOURCES
index 3c9a3068ad..db9e05644c 100644
--- a/bootloader/SOURCES
+++ b/bootloader/SOURCES
@@ -89,4 +89,11 @@ show_logo.c
#elif defined(SANSA_CONNECT)
sansaconnect.c
show_logo.c
+#elif defined(FIIO_M3K)
+#ifdef BOOTLOADER_SPL
+x1000-spl.c
+fiiom3k-spl.c
+#else
+fiiom3k.c
+#endif
#endif
diff --git a/bootloader/fiiom3k-spl.c b/bootloader/fiiom3k-spl.c
new file mode 100644
index 0000000000..ec532d5789
--- /dev/null
+++ b/bootloader/fiiom3k-spl.c
@@ -0,0 +1,204 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "nand-x1000.h"
+#include "gpio-x1000.h"
+#include "mmu-mips.h"
+#include <string.h>
+
+/* "fiio" in little endian */
+#define BOOTMAGIC 0x6f696966
+
+/* Argument structure needed by Linux */
+struct linux_kargs {
+ void* arg0;
+ void* arg1;
+};
+
+#define LINUX_KARGSADDR 0x80004000
+
+static const char recovery_cmdline[] = "mem=xxM@0x0\
+ no_console_suspend\
+ console=ttyS2,115200n8\
+ lpj=5009408\
+ ip=off";
+
+static const char normal_cmdline[] = "mem=64M@0x0\
+ no_console_suspend\
+ console=ttyS2,115200n8\
+ lpj=5009408\
+ ip=off\
+ init=/linuxrc\
+ ubi.mtd=3\
+ root=ubi0:rootfs\
+ ubi.mtd=4\
+ rootfstype=ubifs\
+ rw\
+ loglevel=8";
+
+#define BOOTOPTION_ROCKBOX 0
+#define BOOTOPTION_FIIOLINUX 1
+#define BOOTOPTION_RECOVERY 2
+#define NUM_BOOTOPTIONS 3
+
+static const struct bootoption {
+ uint32_t nand_addr;
+ uint32_t nand_size;
+ unsigned long load_addr;
+ unsigned long exec_addr;
+ const char* cmdline;
+} boot_options[NUM_BOOTOPTIONS] = {
+ {
+ /* Rockbox: the first unused NAND page is 26 KiB in, and the
+ * remainder of the block is unused, giving us 102 KiB to use.
+ */
+ .nand_addr = 0x6800,
+ .nand_size = 0x19800,
+ .load_addr = 0x80003ff8, /* first 8 bytes are bootloader ID */
+ .exec_addr = 0x80004000,
+ .cmdline = NULL,
+ },
+ {
+ /* Original firmware */
+ .nand_addr = 0x20000,
+ .nand_size = 0x400000,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = normal_cmdline,
+ },
+ {
+ /* Recovery image */
+ .nand_addr = 0x420000,
+ .nand_size = 0x500000,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = recovery_cmdline,
+ },
+};
+
+/* Simple diagnostic if something goes wrong -- a little nicer than wondering
+ * what's going on when the machine hangs
+ */
+void die(void)
+{
+ const int pin = (1 << 24);
+
+ /* Turn on button light */
+ jz_clr(GPIO_INT(GPIO_C), pin);
+ jz_set(GPIO_MSK(GPIO_C), pin);
+ jz_clr(GPIO_PAT1(GPIO_C), pin);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ while(1) {
+ /* Turn it off */
+ mdelay(100);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ /* Turn it on */
+ mdelay(100);
+ jz_clr(GPIO_PAT0(GPIO_C), pin);
+ }
+}
+
+/* Boot select button state must remain stable for this duration
+ * before the choice will be accepted. Currently 100ms.
+ */
+#define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000))
+
+int get_boot_option(void)
+{
+ const uint32_t pinmask = (1 << 17) | (1 << 19);
+
+ uint32_t pin = 1, lastpin = 0;
+ uint32_t deadline = 0;
+
+ /* Configure the button GPIOs as inputs */
+ gpio_config(GPIO_A, pinmask, GPIO_INPUT);
+
+ /* Poll the pins for a short duration to detect a keypress */
+ do {
+ lastpin = pin;
+ pin = ~REG_GPIO_PIN(GPIO_A) & pinmask;
+ if(pin != lastpin) {
+ /* This will always be set on the first iteration */
+ deadline = __ost_read32() + BTN_STABLE_TIME;
+ }
+ } while(__ost_read32() < deadline);
+
+ /* Play button boots original firmware */
+ if(pin == (1 << 17))
+ return BOOTOPTION_FIIOLINUX;
+
+ /* Volume up boots recovery */
+ if(pin == (1 << 19))
+ return BOOTOPTION_RECOVERY;
+
+ /* Default is to boot Rockbox */
+ return BOOTOPTION_ROCKBOX;
+}
+
+void spl_main(void)
+{
+ /* Get user boot option */
+ int booti = get_boot_option();
+ const struct bootoption* opt = &boot_options[booti];
+
+ /* Load selected firmware from flash */
+ if(nand_open())
+ die();
+ if(nand_read_bytes(opt->nand_addr, opt->nand_size, (void*)opt->load_addr))
+ die();
+
+ if(booti == BOOTOPTION_ROCKBOX) {
+ /* If bootloader is not installed, return back to boot ROM.
+ * Also read in the first eraseblock of NAND flash so it can be
+ * dumped back over USB.
+ */
+ if(*(unsigned*)(opt->load_addr + 4) != BOOTMAGIC) {
+ nand_read_bytes(0, 128 * 1024, (void*)0x80000000);
+ commit_discard_idcache();
+ return;
+ }
+ } else {
+ /* TODO: Linux boot not implemented yet
+ *
+ * - Have to initialize UART2, as it's used for the serial console
+ * - Must initialize APLL and change clocks over
+ * - There are some other clocks which need to be initialized
+ * - We should turn off OST since the OF SPL does not turn it on
+ */
+ die();
+ }
+
+ if(boot_options[booti].cmdline) {
+ /* Handle Linux command line arguments */
+ struct linux_kargs* kargs = (struct linux_kargs*)LINUX_KARGSADDR;
+ kargs->arg0 = 0;
+ kargs->arg1 = (void*)boot_options[booti].cmdline;
+ }
+
+ /* Flush caches and jump to address */
+ void* execaddr = (void*)opt->exec_addr;
+ commit_discard_idcache();
+ __asm__ __volatile__ ("jr %0" :: "r"(execaddr));
+ __builtin_unreachable();
+}
diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c
new file mode 100644
index 0000000000..6108a37efc
--- /dev/null
+++ b/bootloader/fiiom3k.c
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "kernel/kernel-internal.h"
+#include "i2c.h"
+#include "power.h"
+#include "lcd.h"
+#include "backlight.h"
+#include "button.h"
+#include "storage.h"
+#include "file_internal.h"
+#include "disk.h"
+#include "rb-loader.h"
+#include "loader_strerror.h"
+
+/* Load address where the binary needs to be placed */
+extern unsigned char loadaddress[];
+
+/* Fixed buffer to contain the loaded binary in memory */
+extern unsigned char loadbuffer[];
+extern unsigned char loadbufferend[];
+#define MAX_LOAD_SIZE (loadbufferend - loadbuffer)
+
+void exec(void* dst, const void* src, int bytes)
+ __attribute__((noreturn, section(".icode")));
+
+void exec(void* dst, const void* src, int bytes)
+{
+ memcpy(dst, src, bytes);
+ commit_discard_idcache();
+ __asm__ __volatile__ ("jr %0" :: "r"(dst));
+ __builtin_unreachable();
+}
+
+static void error(const char* msg)
+{
+ /* Initialization of the LCD/buttons only if needed */
+ lcd_init();
+ backlight_init();
+ button_init();
+
+ lcd_clear_display();
+ lcd_puts(0, 0, msg);
+ lcd_puts(0, 2, "Press POWER to power off");
+ lcd_update();
+
+ while(button_get(true) != BUTTON_POWER);
+ power_off();
+}
+
+void main(void)
+{
+ system_init();
+ kernel_init();
+ i2c_init();
+ power_init();
+ enable_irq();
+
+ if(storage_init() < 0)
+ error("Storage initialization failed");
+
+ filesystem_init();
+
+ if(!storage_present(0))
+ error("No SD card present");
+
+ if(disk_mount_all() <= 0)
+ error("Unable to mount filesystem");
+
+ int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
+ if(loadsize <= 0)
+ error(loader_strerror(loadsize));
+
+ disable_irq();
+
+ exec(loadaddress, loadbuffer, loadsize);
+}
diff --git a/bootloader/x1000-spl.c b/bootloader/x1000-spl.c
new file mode 100644
index 0000000000..1c780a9843
--- /dev/null
+++ b/bootloader/x1000-spl.c
@@ -0,0 +1,230 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/ost.h"
+#include "x1000/ddrc.h"
+#include "x1000/ddrc_apb.h"
+#include "x1000/ddrphy.h"
+
+#ifdef FIIO_M3K
+# define DDR_USE_AUTOSR 1
+# define DDR_NEED_BYPASS 1
+# define DDR_MEMORYSIZE 64
+#else
+# error "Please add DDR definitions for new target!"
+#endif
+
+#define hang() do { } while(1)
+
+/* Target-specific routine to load & execute the Rockbox bootloader */
+extern void spl_main(void);
+
+/* Note: This is based purely on disassembly of the SPL from the FiiO M3K.
+ * The code there is somewhat generic and corresponds roughly to Ingenic's
+ * U-Boot code, but isn't entirely the same.
+ *
+ * I converted all the runtime conditionals to compile-time ones in order to
+ * save code space, since they should be constant for any given target.
+ *
+ * I haven't bothered to decode all the register fields. Some of the values
+ * written are going to bits documented as "Reserved" by Ingenic, but their
+ * documentation doesn't seem completely reliable, so either these are bits
+ * which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
+ *
+ * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite".
+ * These aren't documented by Ingenic, but the addresses and names can be found
+ * in their U-Boot code.
+ */
+static void ddr_init(void)
+{
+ REG_CPM_DRCG = 0x73;
+ mdelay(3);
+ REG_CPM_DRCG = 0x71;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0x1a00001;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+ REG_DDRC_CTRL = 0xf00000;
+ mdelay(3);
+ REG_DDRC_CTRL = 0;
+ mdelay(3);
+
+ REG_DDRC_CFG = 0xa468a6c;
+ REG_DDRC_CTRL = 2;
+ REG_DDRPHY_DTAR = 0x150000;
+ REG_DDRPHY_DCR = 0;
+ REG_DDRPHY_MR0 = 0x42;
+ REG_DDRPHY_MR2 = 0x98;
+ REG_DDRPHY_PTR0 = 0x21000a;
+ REG_DDRPHY_PTR1 = 0xa09c40;
+ REG_DDRPHY_PTR2 = 0x280014;
+ REG_DDRPHY_DTPR0 = 0x1a69444a;
+ REG_DDRPHY_DTPR1 = 0x180090;
+ REG_DDRPHY_DTPR2 = 0x1ff99428;
+ REG_DDRPHY_DXGCR(0) = 0x90881;
+ REG_DDRPHY_DXGCR(1) = 0x90881;
+ REG_DDRPHY_DXGCR(2) = 0x90e80;
+ REG_DDRPHY_DXGCR(3) = 0x90e80;
+ REG_DDRPHY_PGCR = 0x1042e03;
+ REG_DDRPHY_ACIOCR = 0x30c00813;
+ REG_DDRPHY_DXCCR = 0x4912;
+
+ int i = 10000;
+ while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ hang();
+
+#if DDR_NEED_BYPASS
+ REG_DDRPHY_ACDLLCR = 0x80000000;
+ REG_DDRPHY_DSGCR &= ~0x10;
+ REG_DDRPHY_DLLGCR |= 0x800000;
+ REG_DDRPHY_PIR = 0x20020041;
+#else
+ REG_DDRPHY_PIR = 0x41;
+#endif
+
+ while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ hang();
+
+ REG_DDRC_APB_PHYRST_CFG = 0x400000;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+
+ REG_DDRC_CFG = 0xa468aec;
+ REG_DDRC_CTRL = 2;
+#if DDR_NEED_BYPASS
+ REG_DDRPHY_PIR = 0x20020081;
+#else
+ REG_DDRPHY_PIR = 0x85;
+#endif
+
+ i = 500000;
+ while(REG_DDRPHY_PGSR != 0x1f) {
+ if(REG_DDRPHY_PGSR & 0x70)
+ break;
+ i -= 1;
+ }
+
+ if(i == 0)
+ hang();
+
+ if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
+ hang();
+
+ REG_DDRC_CTRL = 0;
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_CTRL = 0;
+ REG_DDRC_CFG = 0xa468a6c;
+ REG_DDRC_TIMING1 = 0x2050501;
+ REG_DDRC_TIMING2 = 0x4090404;
+ REG_DDRC_TIMING3 = 0x2704030d;
+ REG_DDRC_TIMING4 = 0xb7a0251;
+ REG_DDRC_TIMING5 = 0xff090200;
+ REG_DDRC_TIMING6 = 0xa0a0202;
+#if DDR_MEMORYSIZE == 64
+ REG_DDRC_MMAP0 = 0x20fc;
+ REG_DDRC_MMAP1 = 0x2400;
+#elif DDR_MEMORYSIZE == 32
+ REG_DDRC_MMAP0 = 0x20fe;
+ REG_DDRC_MMAP1 = 0x2200;
+#else
+# error "Unsupported DDR_MEMORYSIZE"
+#endif
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_REFCNT = 0x2f0003;
+ REG_DDRC_CTRL = 0xc91e;
+
+#if DDR_MEMORYSIZE == 64
+ REG_DDRC_REMAP1 = 0x03020c0b;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x000a0908;
+ REG_DDRC_REMAP4 = 0x0f0e0d01;
+ REG_DDRC_REMAP5 = 0x13121110;
+#elif DDR_MEMORYSIZE == 32
+ REG_DDRC_REMAP1 = 0x03020b0a;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x01000908;
+ REG_DDRC_REMAP4 = 0x0f0e0d0c;
+ REG_DDRC_REMAP5 = 0x13121110;
+#else
+# error "Unsupported DDR_MEMORYSIZE"
+#endif
+
+ REG_DDRC_STATUS &= ~0x40;
+
+#if DDR_USE_AUTOSR
+#if DDR_NEED_BYPASS
+ jz_writef(CPM_DDRCDR, GATE_EN(1));
+ REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
+#else
+ REG_DDRC_DLP = 0;
+#endif
+#endif
+
+ REG_DDRC_AUTOSR_EN = DDR_USE_AUTOSR;
+}
+
+void main(void)
+{
+ /* from original firmware SPL */
+ REG_CPM_PSWC0ST = 0x00;
+ REG_CPM_PSWC1ST = 0x10;
+ REG_CPM_PSWC2ST = 0x18;
+ REG_CPM_PSWC3ST = 0x08;
+
+ /* enable MPLL */
+#if X1000_EXCLK_FREQ == 24000000
+ /* 24 * (24+1) = 600 MHz */
+ jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
+#elif X1000_EXCLK_FREQ == 26000000
+ /* 26 * (22+1) = 598 MHz */
+ jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
+#else
+# error "unknown EXCLK frequency"
+#endif
+ while(jz_readf(CPM_MPCR, ON) == 0);
+
+ /* set DDR clock to MPLL/3 = 200 MHz */
+ jz_writef(CPM_CLKGR, DDR(0));
+ clk_set_ddr(X1000_CLK_MPLL, 3);
+
+ /* start OST so we can use mdelay/udelay */
+ jz_writef(CPM_CLKGR, OST(0));
+ jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
+ jz_writef(OST_CLEAR, OST2(1));
+ jz_write(OST_2CNTH, 0);
+ jz_write(OST_2CNTL, 0);
+ jz_setf(OST_ENABLE, OST2);
+
+ /* init DDR memory */
+ ddr_init();
+
+ /* jump to the target's main routine */
+ spl_main();
+}
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 236933bdb7..3a42381003 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -398,6 +398,8 @@ drivers/rtc/rtc_tcc77x.c
drivers/rtc/rtc_jz4740.c
#elif (CONFIG_RTC == RTC_JZ4760)
drivers/rtc/rtc_jz4760.c
+#elif (CONFIG_RTC == RTC_X1000)
+drivers/rtc/rtc_x1000.c
#elif (CONFIG_RTC == RTC_S35390A)
drivers/rtc/rtc_s35390a.c
#elif (CONFIG_RTC == RTC_S35380A)
@@ -488,6 +490,8 @@ drivers/audio/as3514.c
drivers/audio/tlv320.c
#elif defined(HAVE_AK4537)
drivers/audio/ak4537.c
+#elif defined(HAVE_AK4376)
+drivers/audio/ak4376.c
#elif defined(HAVE_UDA1341)
drivers/audio/uda1341.c
#elif defined(HAVE_CS42L55)
@@ -1721,6 +1725,30 @@ target/mips/ingenic_jz47xx/pcm-jz4760.c
drivers/nand_id.c
#endif /* CONFIG_CPU == JZ4760B */
+#if CONFIG_CPU == X1000
+target/mips/ingenic_x1000/crt0.S
+target/mips/ingenic_x1000/aic-x1000.c
+target/mips/ingenic_x1000/clk-x1000.c
+target/mips/ingenic_x1000/debug-x1000.c
+target/mips/ingenic_x1000/dma-x1000.c
+target/mips/ingenic_x1000/gpio-x1000.c
+target/mips/ingenic_x1000/i2c-x1000.c
+target/mips/ingenic_x1000/kernel-x1000.c
+target/mips/ingenic_x1000/lcd-x1000.c
+target/mips/ingenic_x1000/nand-x1000.c
+target/mips/ingenic_x1000/pcm-x1000.c
+target/mips/ingenic_x1000/pwm-x1000.c
+target/mips/ingenic_x1000/sfc-x1000.c
+target/mips/ingenic_x1000/system-x1000.c
+target/mips/ingenic_x1000/timer-x1000.c
+#if (CONFIG_STORAGE & (STORAGE_SD|STORAGE_MMC|STORAGE_ATA))
+target/mips/ingenic_x1000/msc-x1000.c
+#endif
+#if (CONFIG_STORAGE & STORAGE_SD)
+target/mips/ingenic_x1000/sd-x1000.c
+#endif
+#endif /* CONFIG_CPU == X1000 */
+
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
target/mips/ingenic_jz47xx/onda_vx747/backlight-onda_vx7X7.c
target/mips/ingenic_jz47xx/onda_vx747/lcd-onda_vx747.c
@@ -1744,6 +1772,16 @@ target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c
target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c
#endif /* XDUOO_X3 */
+#if defined(FIIO_M3K)
+target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
+target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+#endif /* FIIO_M3K */
+
#if defined(LYRE_PROTO1)
target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c
target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c
@@ -1969,6 +2007,10 @@ drivers/touchpad.c
drivers/i2c-async.c
#endif
+#ifdef HAVE_AXP173
+drivers/axp173.c
+#endif
+
/* firmware/kernel section */
#ifdef HAVE_CORELOCK_OBJECT
kernel/corelock.c
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
new file mode 100644
index 0000000000..494bbabfa4
--- /dev/null
+++ b/firmware/drivers/audio/ak4376.c
@@ -0,0 +1,274 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "audiohw.h"
+#include "sound.h"
+#include "panic.h"
+#include "pcm_sampr.h"
+#include "pcm_sw_volume.h"
+#include "system.h"
+#include "i2c-async.h"
+
+#ifndef HAVE_SW_VOLUME_CONTROL
+# error "AK4376 requires HAVE_SW_VOLUME_CONTROL!"
+#endif
+
+/* NOTE: At present, only the FiiO M3K uses this driver so the handling of
+ * the clock / audio interface is limited to I2S slave, 16-bit samples, with
+ * DAC master clock provided directly on the MCLK input pin, fitting the
+ * clock setup of the M3K.
+ *
+ * Feel free to expand upon this if another target ever needs this driver.
+ */
+
+/* Converts HW_FREQ_XX constants to register values */
+static const int ak4376_fsel_to_hw[] = {
+ HW_HAVE_192_(AK4376_FS_192,)
+ HW_HAVE_176_(AK4376_FS_176,)
+ HW_HAVE_96_(AK4376_FS_96,)
+ HW_HAVE_88_(AK4376_FS_88,)
+ HW_HAVE_64_(AK4376_FS_64,)
+ HW_HAVE_48_(AK4376_FS_48,)
+ HW_HAVE_44_(AK4376_FS_44,)
+ HW_HAVE_32_(AK4376_FS_32,)
+ HW_HAVE_24_(AK4376_FS_24,)
+ HW_HAVE_22_(AK4376_FS_22,)
+ HW_HAVE_16_(AK4376_FS_16,)
+ HW_HAVE_12_(AK4376_FS_12,)
+ HW_HAVE_11_(AK4376_FS_11,)
+ HW_HAVE_8_(AK4376_FS_8,)
+};
+
+static struct ak4376 {
+ int fsel;
+ int low_mode;
+ int regs[AK4376_NUM_REGS];
+} ak4376;
+
+void ak4376_init(void)
+{
+ /* Initialize DAC state */
+ ak4376.fsel = HW_FREQ_48;
+ ak4376.low_mode = 0;
+ for(int i = 0; i < AK4376_NUM_REGS; ++i)
+ ak4376.regs[i] = -1;
+
+ /* Initial reset after power-on */
+ ak4376_set_pdn_pin(0);
+ mdelay(1);
+ ak4376_set_pdn_pin(1);
+ mdelay(1);
+
+ static const int init_config[] = {
+ /* Ensure HPRHZ, HPLHZ are 0 */
+ AK4376_REG_OUTPUT_MODE, 0x00,
+ /* Mute all volume controls */
+ AK4376_REG_MIXER, 0x00,
+ AK4376_REG_LCH_VOLUME, 0x80,
+ AK4376_REG_RCH_VOLUME, 0x00,
+ AK4376_REG_AMP_VOLUME, 0x00,
+ /* Clock source = MCLK, divider = 1 */
+ AK4376_REG_DAC_CLK_SRC, 0x00,
+ AK4376_REG_DAC_CLK_DIV, 0x00,
+ /* I2S slave mode, 16-bit samples */
+ AK4376_REG_AUDIO_IF_FMT, 0x03,
+ /* Recommended by datasheet */
+ AK4376_REG_ADJUST1, 0x20,
+ AK4376_REG_ADJUST2, 0x05,
+ /* Power controls */
+ AK4376_REG_PWR2, 0x33,
+ AK4376_REG_PWR3, 0x01,
+ AK4376_REG_PWR4, 0x03,
+ };
+
+ /* Write initial configuration prior to power-up */
+ for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
+ ak4376_write(init_config[i], init_config[i+1]);
+
+ /* Initial frequency setting, also handles DAC/amp power-up */
+ audiohw_set_frequency(HW_FREQ_48);
+}
+
+void ak4376_close(void)
+{
+ /* Shut off power */
+ ak4376_write(AK4376_REG_PWR3, 0x00);
+ ak4376_write(AK4376_REG_PWR4, 0x00);
+ ak4376_write(AK4376_REG_PWR2, 0x00);
+
+ /* PDN pin low */
+ ak4376_set_pdn_pin(0);
+}
+
+void ak4376_write(int reg, int value)
+{
+ /* Ensure value is sensible and differs from the last set value */
+ if((value & 0xff) == value && ak4376.regs[reg] != value) {
+ int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value);
+ if(r == I2C_STATUS_OK)
+ ak4376.regs[reg] = value;
+ else
+ ak4376.regs[reg] = -1;
+ }
+}
+
+int ak4376_read(int reg)
+{
+ /* Only read from I2C if we don't already know the value */
+ if(ak4376.regs[reg] < 0)
+ ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
+
+ return ak4376.regs[reg];
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+static void calc_volumes(int vol, int* mix, int* dig, int* sw)
+{
+ /* Mixer can divide by 2, which gives an extra -6 dB adjustment */
+ if(vol < AK4376_DIG_VOLUME_MIN) {
+ *mix |= AK4376_MIX_HALF;
+ vol += 60;
+ }
+
+ *dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP);
+ *dig = MIN(*dig, AK4376_DIG_VOLUME_MAX);
+ *dig = MAX(*dig, AK4376_DIG_VOLUME_MIN);
+ vol -= *dig;
+
+ /* Seems that this is the allowable range for software volume */
+ *sw = MIN(vol, 60);
+ *sw = MAX(*sw, -730);
+ vol -= *sw;
+}
+
+static int dig_vol_to_hw(int vol)
+{
+ if(vol < AK4376_DIG_VOLUME_MIN) return 0;
+ if(vol > AK4376_DIG_VOLUME_MAX) return 31;
+ return (vol - AK4376_DIG_VOLUME_MIN) / AK4376_DIG_VOLUME_STEP + 1;
+}
+
+static int amp_vol_to_hw(int vol)
+{
+ if(vol < AK4376_AMP_VOLUME_MIN) return 0;
+ if(vol > AK4376_AMP_VOLUME_MAX) return 14;
+ return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1;
+}
+
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ int amp;
+ int mix_l = AK4376_MIX_LCH, dig_l, sw_l;
+ int mix_r = AK4376_MIX_RCH, dig_r, sw_r;
+
+ if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) {
+ /* Special case for full mute */
+ amp = AK4376_AMP_VOLUME_MUTE;
+ dig_l = dig_r = AK4376_DIG_VOLUME_MUTE;
+ sw_l = sw_r = PCM_MUTE_LEVEL;
+ } else {
+ /* Amp is a mono control -- calculate based on the loudest channel.
+ * The quieter channel then gets reduced more by digital controls. */
+ amp = round_step_up(MAX(vol_l, vol_r), AK4376_AMP_VOLUME_STEP);
+ amp = MIN(amp, AK4376_AMP_VOLUME_MAX);
+ amp = MAX(amp, AK4376_AMP_VOLUME_MIN);
+
+ /* Other controls are stereo */
+ calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l);
+ calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r);
+ }
+
+ ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4));
+ ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7));
+ ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r));
+ ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp));
+ pcm_set_master_volume(sw_l, sw_r);
+}
+
+void audiohw_set_filter_roll_off(int val)
+{
+ int reg = ak4376_read(AK4376_REG_FILTER);
+ reg &= ~0xc0;
+ reg |= (val & 3) << 6;
+ ak4376_write(AK4376_REG_FILTER, reg);
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ /* Determine master clock multiplier */
+ int mult = ak4376_set_mclk_freq(fsel, false);
+
+ /* Calculate clock mode for frequency. Multipliers of 32/64 are only
+ * for rates >= 256 KHz which are not supported by Rockbox, so they
+ * are commented out -- but they're in the correct place. */
+ int clock_mode = ak4376_fsel_to_hw[fsel];
+ switch(mult) {
+ /* case 32: */
+ case 256:
+ break;
+ /* case 64: */
+ case 512:
+ clock_mode |= 0x20;
+ break;
+ case 1024:
+ clock_mode |= 0x40;
+ break;
+ case 128:
+ clock_mode |= 0x60;
+ break;
+ default:
+ panicf("ak4376: bad master clock multiple %d", mult);
+ return;
+ }
+
+ /* Handle the DSMLP bit in the MODE_CTRL register */
+ int mode_ctrl = 0x00;
+ if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12)
+ mode_ctrl |= 0x40;
+
+ /* Program the new settings */
+ ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
+ ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
+ ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01);
+
+ /* Enable the master clock */
+ ak4376_set_mclk_freq(fsel, true);
+
+ /* Remember the frequency */
+ ak4376.fsel = fsel;
+}
+
+void audiohw_set_power_mode(int mode)
+{
+ /* This is handled via audiohw_set_frequency() since changing LPMODE
+ * bit requires power-down/power-up & changing other bits as well */
+ if(ak4376.low_mode != mode) {
+ ak4376.low_mode = mode;
+ audiohw_set_frequency(ak4376.fsel);
+ }
+}
diff --git a/firmware/drivers/axp173.c b/firmware/drivers/axp173.c
new file mode 100644
index 0000000000..22417650fc
--- /dev/null
+++ b/firmware/drivers/axp173.c
@@ -0,0 +1,419 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "axp173.h"
+#include "power.h"
+#include "i2c-async.h"
+
+/* Headers for the debug menu */
+#ifndef BOOTLOADER
+# include "action.h"
+# include "list.h"
+# include <stdio.h>
+#endif
+
+static const struct axp173_adc_info {
+ uint8_t reg;
+ uint8_t en_reg;
+ uint8_t en_bit;
+} axp173_adc_info[NUM_ADC_CHANNELS] = {
+ {0x56, 0x82, 5}, /* ACIN_VOLTAGE */
+ {0x58, 0x82, 4}, /* ACIN_CURRENT */
+ {0x5a, 0x82, 3}, /* VBUS_VOLTAGE */
+ {0x5c, 0x82, 2}, /* VBUS_CURRENT */
+ {0x5e, 0x83, 7}, /* INTERNAL_TEMP */
+ {0x62, 0x82, 1}, /* TS_INPUT */
+ {0x78, 0x82, 7}, /* BATTERY_VOLTAGE */
+ {0x7a, 0x82, 6}, /* CHARGE_CURRENT */
+ {0x7c, 0x82, 6}, /* DISCHARGE_CURRENT */
+ {0x7e, 0x82, 1}, /* APS_VOLTAGE */
+ {0x70, 0xff, 0}, /* BATTERY_POWER */
+};
+
+static struct axp173 {
+ int adc_enable;
+} axp173;
+
+static void axp173_init_enabled_adcs(void)
+{
+ axp173.adc_enable = 0;
+
+ /* Read enabled ADCs from the hardware */
+ uint8_t regs[2];
+ int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
+ if(rc != I2C_STATUS_OK)
+ return;
+
+ /* Parse registers to set ADC enable bits */
+ const struct axp173_adc_info* info = axp173_adc_info;
+ for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
+ if(info[i].en_reg == 0xff)
+ continue;
+
+ if(regs[info[i].en_reg - 0x82] & info[i].en_bit)
+ axp173.adc_enable |= 1 << i;
+ }
+
+ /* Handle battery power ADC */
+ if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
+ (axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
+ axp173.adc_enable |= (1 << ADC_BATTERY_POWER);
+ }
+}
+
+void axp173_init(void)
+{
+ axp173_init_enabled_adcs();
+
+ /* We need discharge current ADC to reliably poll for a full battery */
+ int bits = axp173.adc_enable;
+ bits |= (1 << ADC_DISCHARGE_CURRENT);
+ axp173_adc_set_enabled(bits);
+}
+
+/* TODO: this can STILL indicate some false positives! */
+int axp173_battery_status(void)
+{
+ int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
+ if(r >= 0) {
+ /* Charging bit indicates we're currently charging */
+ if((r & 0x04) != 0)
+ return AXP173_BATT_CHARGING;
+
+ /* Not plugged in means we're discharging */
+ if((r & 0xf0) == 0)
+ return AXP173_BATT_DISCHARGING;
+ } else {
+ /* Report discharging if we can't find out power status */
+ return AXP173_BATT_DISCHARGING;
+ }
+
+ /* If the battery is full and not in use, the charging bit will be 0,
+ * there will be an external power source, AND the discharge current
+ * will be zero. Seems to rule out all false positives. */
+ int d = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT);
+ if(d == 0)
+ return AXP173_BATT_FULL;
+
+ return AXP173_BATT_DISCHARGING;
+}
+
+int axp173_input_status(void)
+{
+#ifdef HAVE_BATTERY_SWITCH
+ int input_status = 0;
+#else
+ int input_status = AXP173_INPUT_BATTERY;
+#endif
+
+ int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
+ if(r < 0)
+ return input_status;
+
+ /* Check for AC input */
+ if(r & 0x80)
+ input_status |= AXP173_INPUT_AC;
+
+ /* Only report USB if ACIN and VBUS are not shorted */
+ if((r & 0x20) != 0 && (r & 0x02) == 0)
+ input_status |= AXP173_INPUT_USB;
+
+#ifdef HAVE_BATTERY_SWITCH
+ /* Check for battery presence if target defines it as removable */
+ r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x01);
+ if(r >= 0 && (r & 0x20) != 0)
+ input_status |= AXP173_INPUT_BATTERY;
+#endif
+
+ return input_status;
+}
+
+int axp173_adc_read(int adc)
+{
+ int value = axp173_adc_read_raw(adc);
+ if(value == INT_MIN)
+ return INT_MIN;
+
+ return axp173_adc_conv_raw(adc, value);
+}
+
+int axp173_adc_read_raw(int adc)
+{
+ /* Don't give a reading if the ADC is not enabled */
+ if((axp173.adc_enable & (1 << adc)) == 0)
+ return INT_MIN;
+
+ /* Read the ADC */
+ uint8_t buf[3];
+ int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
+ uint8_t reg = axp173_adc_info[adc].reg;
+ int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, reg, count, &buf[0]);
+ if(rc != I2C_STATUS_OK)
+ return INT_MIN;
+
+ /* Parse the value */
+ if(adc == ADC_BATTERY_POWER)
+ return (buf[0] << 16) | (buf[1] << 8) | buf[2];
+ else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
+ return (buf[0] << 5) | (buf[1] & 0x1f);
+ else
+ return (buf[0] << 4) | (buf[1] & 0xf);
+}
+
+int axp173_adc_conv_raw(int adc, int value)
+{
+ switch(adc) {
+ case ADC_ACIN_VOLTAGE:
+ case ADC_VBUS_VOLTAGE:
+ /* 0 mV ... 6.9615 mV, step 1.7 mV */
+ return value * 17 / 10;
+ case ADC_ACIN_CURRENT:
+ /* 0 mA ... 2.5594 A, step 0.625 mA */
+ return value * 5 / 8;
+ case ADC_VBUS_CURRENT:
+ /* 0 mA ... 1.5356 A, step 0.375 mA */
+ return value * 3 / 8;
+ case ADC_INTERNAL_TEMP:
+ /* -144.7 C ... 264.8 C, step 0.1 C */
+ return value - 1447;
+ case ADC_TS_INPUT:
+ /* 0 mV ... 3.276 V, step 0.8 mV */
+ return value * 4 / 5;
+ case ADC_BATTERY_VOLTAGE:
+ /* 0 mV ... 4.5045 V, step 1.1 mV */
+ return value * 11 / 10;
+ case ADC_CHARGE_CURRENT:
+ case ADC_DISCHARGE_CURRENT:
+ /* 0 mA to 4.095 A, step 0.5 mA */
+ return value / 2;
+ case ADC_APS_VOLTAGE:
+ /* 0 mV to 5.733 V, step 1.4 mV */
+ return value * 7 / 5;
+ case ADC_BATTERY_POWER:
+ /* 0 uW to 23.6404 W, step 0.55 uW */
+ return value * 11 / 20;
+ default:
+ /* Shouldn't happen */
+ return INT_MIN;
+ }
+}
+
+int axp173_adc_get_enabled(void)
+{
+ return axp173.adc_enable;
+}
+
+void axp173_adc_set_enabled(int adc_bits)
+{
+ /* Ignore no-op */
+ if(adc_bits == axp173.adc_enable)
+ return;
+
+ /* Compute the new register values */
+ const struct axp173_adc_info* info = axp173_adc_info;
+ uint8_t regs[2] = {0, 0};
+ for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
+ if(info[i].en_reg == 0xff)
+ continue;
+
+ if(adc_bits & (1 << i))
+ regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
+ }
+
+ /* These ADCs share an enable bit */
+ if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
+ adc_bits |= (1 << ADC_CHARGE_CURRENT);
+ adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
+ }
+
+ /* Enable required bits for battery power ADC */
+ if(adc_bits & (1 << ADC_BATTERY_POWER)) {
+ regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
+ regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
+ }
+
+ /* Update the configuration */
+ i2c_reg_write(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
+ axp173.adc_enable = adc_bits;
+}
+
+int axp173_adc_get_rate(void)
+{
+ int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x84);
+ if(r < 0)
+ return AXP173_ADC_RATE_100HZ; /* an arbitrary value */
+
+ return (r >> 6) & 3;
+}
+
+void axp173_adc_set_rate(int rate)
+{
+ i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x84,
+ 0xc0, (rate & 3) << 6, NULL);
+}
+
+static uint32_t axp173_cc_parse(const uint8_t* buf)
+{
+ return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+void axp173_cc_read(uint32_t* charge, uint32_t* discharge)
+{
+ uint8_t buf[8];
+ int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0xb0, 8, &buf[0]);
+ if(rc != I2C_STATUS_OK) {
+ if(charge)
+ *charge = 0;
+ if(discharge)
+ *discharge = 0;
+ return;
+ }
+
+ if(charge)
+ *charge = axp173_cc_parse(&buf[0]);
+ if(discharge)
+ *discharge = axp173_cc_parse(&buf[4]);
+}
+
+void axp173_cc_clear(void)
+{
+ i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 5, 1, NULL);
+}
+
+void axp173_cc_enable(bool en)
+{
+ i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 7, en ? 1 : 0, NULL);
+}
+
+#ifndef BOOTLOADER
+#define AXP173_DEBUG_BATTERY_STATUS 0
+#define AXP173_DEBUG_INPUT_STATUS 1
+#define AXP173_DEBUG_ADC_RATE 2
+#define AXP173_DEBUG_FIRST_ADC 3
+#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS)
+
+static int axp173_debug_menu_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+
+ if(action == ACTION_NONE)
+ action = ACTION_REDRAW;
+
+ return action;
+}
+
+static const char* axp173_debug_menu_get_name(int item, void* data,
+ char* buf, size_t buflen)
+{
+ (void)data;
+
+ static const char* const adc_names[] = {
+ "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
+ "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
+ };
+
+ static const char* const adc_units[] = {
+ "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
+ };
+
+ int adc = item - AXP173_DEBUG_FIRST_ADC;
+ if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
+ int raw_value = axp173_adc_read_raw(adc);
+ if(raw_value == INT_MIN) {
+ snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
+ return buf;
+ }
+
+ int value = axp173_adc_conv_raw(adc, raw_value);
+ if(adc == ADC_INTERNAL_TEMP) {
+ snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
+ value/10, value%10, adc_units[adc]);
+ } else {
+ snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
+ value, adc_units[adc]);
+ }
+
+ return buf;
+ }
+
+ switch(item) {
+ case AXP173_DEBUG_BATTERY_STATUS: {
+ switch(axp173_battery_status()) {
+ case AXP173_BATT_FULL:
+ return "Battery: Full";
+ case AXP173_BATT_CHARGING:
+ return "Battery: Charging";
+ case AXP173_BATT_DISCHARGING:
+ return "Battery: Discharging";
+ default:
+ return "Battery: Unknown";
+ }
+ } break;
+
+ case AXP173_DEBUG_INPUT_STATUS: {
+ int s = axp173_input_status();
+ const char* ac = (s & AXP173_INPUT_AC) ? " AC" : "";
+ const char* usb = (s & AXP173_INPUT_USB) ? " USB" : "";
+ const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : "";
+ snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
+ return buf;
+ } break;
+
+ case AXP173_DEBUG_ADC_RATE: {
+ int rate = 25 << axp173_adc_get_rate();
+ snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
+ return buf;
+ } break;
+
+ default:
+ return "---";
+ }
+}
+
+bool axp173_debug_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL);
+ info.action_callback = axp173_debug_menu_cb;
+ info.get_name = axp173_debug_menu_get_name;
+ return simplelist_show_list(&info);
+}
+#endif /* !BOOTLOADER */
+
+/* This is basically the only valid implementation, so define it here */
+unsigned int power_input_status(void)
+{
+ unsigned int state = 0;
+ int input_status = axp173_input_status();
+
+ if(input_status & AXP173_INPUT_AC)
+ state |= POWER_INPUT_MAIN_CHARGER;
+
+ if(input_status & AXP173_INPUT_USB)
+ state |= POWER_INPUT_USB_CHARGER;
+
+#ifdef HAVE_BATTERY_SWITCH
+ if(input_status & AXP173_INPUT_BATTERY)
+ state |= POWER_INPUT_BATTERY;
+#endif
+
+ return state;
+}
diff --git a/firmware/drivers/rtc/rtc_x1000.c b/firmware/drivers/rtc/rtc_x1000.c
new file mode 100644
index 0000000000..1d3a5484e9
--- /dev/null
+++ b/firmware/drivers/rtc/rtc_x1000.c
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * Based mainly on rtc_jz4760.c,
+ * Copyright (C) 2016 by Roman Stolyarov
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "rtc.h"
+#include "x1000/rtc.h"
+#include <stdint.h>
+
+/* 4 byte magic number 'RTCV' */
+#define RTCV 0x52544356
+
+/* expected RTC clock frequency */
+#define NC1HZ_EXPECTED (32768 - 1)
+
+static void rtc_write_reg(uint32_t addr, uint32_t value)
+{
+ while(jz_readf(RTC_CR, WRDY) == 0);
+ REG_RTC_WENR = 0xa55a;
+ while(jz_readf(RTC_WENR, WEN) == 0);
+ while(jz_readf(RTC_CR, WRDY) == 0);
+ (*(volatile uint32_t*)addr) = value;
+ while(jz_readf(RTC_CR, WRDY) == 0);
+}
+
+void rtc_init(void)
+{
+ /* Check if we totally lost power and need to reset the RTC */
+ if(REG_RTC_HSPR != RTCV || jz_readf(RTC_GR, NC1HZ) != NC1HZ_EXPECTED) {
+ rtc_write_reg(JA_RTC_GR, NC1HZ_EXPECTED);
+ rtc_write_reg(JA_RTC_HWFCR, 3200);
+ rtc_write_reg(JA_RTC_HRCR, 2048);
+ rtc_write_reg(JA_RTC_SR, 1546300800); /* 01/01/2019 */
+ rtc_write_reg(JA_RTC_CR, 1);
+ rtc_write_reg(JA_RTC_HSPR, RTCV);
+ }
+
+ rtc_write_reg(JA_RTC_HWRSR, 0);
+}
+
+int rtc_read_datetime(struct tm* tm)
+{
+ time_t time = REG_RTC_SR;
+ gmtime_r(&time, tm);
+ return 1;
+}
+
+int rtc_write_datetime(const struct tm* tm)
+{
+ time_t time = mktime((struct tm*)tm);
+ rtc_write_reg(JA_RTC_SR, time);
+ return 1;
+}
+
+#ifdef HAVE_RTC_ALARM
+/* TODO: implement the RTC alarm */
+
+void rtc_set_alarm(int h, int m)
+{
+ (void)h;
+ (void)m;
+}
+
+void rtc_get_alarm(int* h, int* m)
+{
+ (void)h;
+ (void)m;
+}
+
+void rtc_enable_alarm(bool enable)
+{
+ (void)enable;
+}
+
+bool rtc_check_alarm_started(bool release_alarm)
+{
+ (void)release_alarm;
+ return false;
+}
+
+bool rtc_check_alarm_flag(void)
+{
+ return false;
+}
+#endif
diff --git a/firmware/export/ak4376.h b/firmware/export/ak4376.h
new file mode 100644
index 0000000000..eb06755e92
--- /dev/null
+++ b/firmware/export/ak4376.h
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __AK4376_H__
+#define __AK4376_H__
+
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
+#define AUDIOHW_HAVE_SHORT2_ROLL_OFF
+
+#define AK4376_MIN_VOLUME (-890)
+#define AK4376_MAX_VOLUME 150
+
+AUDIOHW_SETTING(VOLUME, "dB", 1, 5, AK4376_MIN_VOLUME, AK4376_MAX_VOLUME, -200)
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 3, 0)
+AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0)
+
+/* Register addresses */
+#define AK4376_REG_PWR1 0x00
+#define AK4376_REG_PWR2 0x01
+#define AK4376_REG_PWR3 0x02
+#define AK4376_REG_PWR4 0x03
+#define AK4376_REG_OUTPUT_MODE 0x04
+#define AK4376_REG_CLOCK_MODE 0x05
+#define AK4376_REG_FILTER 0x06
+#define AK4376_REG_MIXER 0x07
+#define AK4376_REG_LCH_VOLUME 0x0b
+#define AK4376_REG_RCH_VOLUME 0x0c
+#define AK4376_REG_AMP_VOLUME 0x0d
+#define AK4376_REG_PLL_CLK_SRC 0x0e
+#define AK4376_REG_PLL_REF_DIV1 0x0f
+#define AK4376_REG_PLL_REF_DIV2 0x10
+#define AK4376_REG_PLL_FB_DIV1 0x11
+#define AK4376_REG_PLL_FB_DIV2 0x12
+#define AK4376_REG_DAC_CLK_SRC 0x13
+#define AK4376_REG_DAC_CLK_DIV 0x14
+#define AK4376_REG_AUDIO_IF_FMT 0x15
+#define AK4376_REG_CHIP_ID 0x21
+#define AK4376_REG_MODE_CTRL 0x24
+#define AK4376_REG_ADJUST1 0x26
+#define AK4376_REG_ADJUST2 0x2a
+#define AK4376_NUM_REGS 0x2b
+
+/* Mixer controls, simply OR them together.
+ * LCH = add LCH signal to output
+ * RCH = add RCH signal to output
+ * HALF = multiply output by 1/2
+ * INVERT = invert the output after everything else
+ */
+#define AK4376_MIX_MUTE 0
+#define AK4376_MIX_LCH 1
+#define AK4376_MIX_RCH 2
+#define AK4376_MIX_HALF 4
+#define AK4376_MIX_INVERT 8
+
+/* Min/max digital volumes in units of dB/10 */
+#define AK4376_DIG_VOLUME_MIN (-120)
+#define AK4376_DIG_VOLUME_MAX 30
+#define AK4376_DIG_VOLUME_STEP 5
+#define AK4376_DIG_VOLUME_MUTE (AK4376_DIG_VOLUME_MIN - 1)
+
+/* Min/max headphone amp volumes in units of dB/10 */
+#define AK4376_AMP_VOLUME_MIN (-200)
+#define AK4376_AMP_VOLUME_MAX 60
+#define AK4376_AMP_VOLUME_STEP 20
+#define AK4376_AMP_VOLUME_MUTE (AK4376_AMP_VOLUME_MIN - 1)
+
+/* Digital filters */
+#define AK4376_FILTER_SHARP 0
+#define AK4376_FILTER_SLOW 1
+#define AK4376_FILTER_SHORT_SHARP 2
+#define AK4376_FILTER_SHORT_SLOW 3
+
+/* Frequency selection */
+#define AK4376_FS_8 0
+#define AK4376_FS_11 1
+#define AK4376_FS_12 2
+#define AK4376_FS_16 4
+#define AK4376_FS_22 5
+#define AK4376_FS_24 6
+#define AK4376_FS_32 8
+#define AK4376_FS_44 9
+#define AK4376_FS_48 10
+#define AK4376_FS_64 12
+#define AK4376_FS_88 13
+#define AK4376_FS_96 14
+#define AK4376_FS_176 17
+#define AK4376_FS_192 18
+
+/* Functions to power on / off the DAC which should be called from
+ * the target's audiohw_init() / audiohw_close() implementation.
+ */
+extern void ak4376_init(void);
+extern void ak4376_close(void);
+
+/* Register read/write. Cached to avoid redundant reads/writes. */
+extern void ak4376_write(int reg, int value);
+extern int ak4376_read(int reg);
+
+/* Target-specific function to set the PDN pin level. */
+extern void ak4376_set_pdn_pin(int level);
+
+/* Target-specific function to control the external master clock frequency.
+ * This is called by the ak4376's audiohw implementation when switching to
+ * or from a frequency that is configured to use this clock source.
+ *
+ * - hw_freq is the new sample rate -- one of the HW_FREQ_XX constants.
+ * - enabled is true if clock should be output, false if not.
+ *
+ * The return value is the master clock rate as a multiple of the sampling
+ * frequency. The allowed multiples depend on the sampling frequency, shown
+ * in the table below.
+ *
+ * +-----------+------------------------+
+ * | frequency | master clock rate |
+ * +-----------+------------------------+
+ * | 8 - 24 | 256fs / 512fs / 1024fs |
+ * | 32 - 48 | 256fs / 512fs |
+ * | 64 - 96 | 256fs |
+ * | 128 - 192 | 128fs |
+ * +-----------+------------------------+
+ *
+ * For example, at 48 KHz you could return either 256 or 512 depending on
+ * the rate you decided to actually use.
+ *
+ * You need to return a valid master multiplier for supported frequencies
+ * even when enabled = false, since the driver needs to know the multiplier
+ * _before_ enabling the clock.
+ *
+ * For unsupported frequencies you don't need to return a valid master
+ * multiplier, because the DAC doesn't need the return value in such cases.
+ */
+extern int ak4376_set_mclk_freq(int hw_freq, bool enabled);
+
+#endif /* __AK4376_H__ */
diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h
index d862c977db..ceafc6ebf7 100644
--- a/firmware/export/audiohw.h
+++ b/firmware/export/audiohw.h
@@ -194,6 +194,8 @@ struct sound_settings_info
#include "jz4740-codec.h"
#elif defined(HAVE_AK4537)
#include "ak4537.h"
+#elif defined(HAVE_AK4376)
+#include "ak4376.h"
#elif defined(HAVE_RK27XX_CODEC)
#include "rk27xx_codec.h"
#elif defined(HAVE_AIC3X)
diff --git a/firmware/export/axp173.h b/firmware/export/axp173.h
new file mode 100644
index 0000000000..60519138e1
--- /dev/null
+++ b/firmware/export/axp173.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __AXP173_H__
+#define __AXP173_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define ADC_ACIN_VOLTAGE 0
+#define ADC_ACIN_CURRENT 1
+#define ADC_VBUS_VOLTAGE 2
+#define ADC_VBUS_CURRENT 3
+#define ADC_INTERNAL_TEMP 4
+#define ADC_TS_INPUT 5
+#define ADC_BATTERY_VOLTAGE 6
+#define ADC_CHARGE_CURRENT 7
+#define ADC_DISCHARGE_CURRENT 8
+#define ADC_APS_VOLTAGE 9
+#define ADC_BATTERY_POWER 10
+#define NUM_ADC_CHANNELS 11
+
+/* ADC sampling rates */
+#define AXP173_ADC_RATE_25HZ 0
+#define AXP173_ADC_RATE_50HZ 1
+#define AXP173_ADC_RATE_100HZ 2
+#define AXP173_ADC_RATE_200HZ 3
+
+/* Return values of axp173_battery_status() */
+#define AXP173_BATT_DISCHARGING 0
+#define AXP173_BATT_CHARGING 1
+#define AXP173_BATT_FULL 2
+
+/* Bits returned by axp173_input_status() */
+#define AXP173_INPUT_AC (1 << 0)
+#define AXP173_INPUT_USB (1 << 1)
+#define AXP173_INPUT_BATTERY (1 << 2)
+#define AXP173_INPUT_EXTERNAL (AXP173_INPUT_AC|AXP173_INPUT_USB)
+
+/* Must be called from power_init() to initialize the driver state */
+extern void axp173_init(void);
+
+/* Basic battery and power supply status */
+extern int axp173_battery_status(void);
+extern int axp173_input_status(void);
+
+/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
+ * The output of axp173_adc_read() is normalized to appropriate units:
+ *
+ * - for voltages, the scale is millivolts
+ * - for currents, the scale is milliamps
+ * - for temperatures, the scale is tenths of a degree Celsius
+ * - for power, the scale is microwatts
+ *
+ * See the comment in axp173_adc_conv_raw() for raw value precision/scale.
+ */
+extern int axp173_adc_read(int adc);
+extern int axp173_adc_read_raw(int adc);
+extern int axp173_adc_conv_raw(int adc, int value);
+extern int axp173_adc_get_enabled(void);
+extern void axp173_adc_set_enabled(int adc_bits);
+extern int axp173_adc_get_rate(void);
+extern void axp173_adc_set_rate(int rate);
+
+/* - axp173_cc_read() reads the coulomb counters
+ * - axp173_cc_clear() resets both counters to zero
+ * - axp173_cc_enable() will stop/start the counters running
+ */
+extern void axp173_cc_read(uint32_t* charge, uint32_t* discharge);
+extern void axp173_cc_clear(void);
+extern void axp173_cc_enable(bool en);
+
+/* Debug menu */
+extern bool axp173_debug_menu(void);
+
+#endif /* __AXP173_H__ */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index e35d407372..db1c589043 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -82,6 +82,7 @@
#define AS3525v2 35252
#define IMX233 233
#define RK27XX 2700
+#define X1000 1000
/* platforms
* bit fields to allow PLATFORM_HOSTED to be OR'ed e.g. with a
@@ -165,6 +166,7 @@
#define XDUOO_X20_PAD 70
#define FIIO_M3K_LINUX_PAD 71
#define EROSQ_PAD 72
+#define FIIO_M3K_PAD 73
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
@@ -281,6 +283,7 @@
#define LCD_IHIFI770 66 /* as used by IHIFI 770 */
#define LCD_IHIFI770C 67 /* as used by IHIFI 770C */
#define LCD_IHIFI800 68 /* as used by IHIFI 800 */
+#define LCD_FIIOM3K 69 /* as used by the FiiO M3K */
/* LCD_PIXELFORMAT */
#define HORIZONTAL_PACKING 1
@@ -320,6 +323,7 @@ Lyre prototype 1 */
#define I2C_S5L8702 16 /* Same as S5L8700, but with two channels */
#define I2C_IMX233 17
#define I2C_RK27XX 18
+#define I2C_X1000 19
/* CONFIG_LED */
#define LED_REAL 1 /* SW controlled LED (Archos recorders, player) */
@@ -356,6 +360,7 @@ Lyre prototype 1 */
#define RTC_IMX233 20
#define RTC_STM41T62 21 /* ST M41T62 */
#define RTC_JZ4760 22 /* Ingenic Jz4760 */
+#define RTC_X1000 23 /* Ingenic X1000 */
/* USB On-the-go */
#define USBOTG_M66591 6591 /* M:Robe 500 */
@@ -605,6 +610,8 @@ Lyre prototype 1 */
#include "config/xduoox20.h"
#elif defined(FIIO_M3K_LINUX)
#include "config/fiiom3klinux.h"
+#elif defined(FIIO_M3K)
+#include "config/fiiom3k.h"
#elif defined(EROS_Q)
#include "config/aigoerosq.h"
#else
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h
new file mode 100644
index 0000000000..68186d7aaf
--- /dev/null
+++ b/firmware/export/config/fiiom3k.h
@@ -0,0 +1,116 @@
+/* RoLo-related defines */
+#define MODEL_NAME "FiiO M3K"
+#define MODEL_NUMBER 114
+#define BOOTFILE_EXT "m3k"
+#define BOOTFILE "rockbox." BOOTFILE_EXT
+#define BOOTDIR "/.rockbox"
+#define FIRMWARE_OFFSET_FILE_CRC 0
+#define FIRMWARE_OFFSET_FILE_DATA 8
+
+/* CPU defines */
+#define CONFIG_CPU X1000
+#define X1000_EXCLK_FREQ 24000000
+#define TIMER_FREQ X1000_EXCLK_FREQ
+#define CPU_FREQ 1008000000
+#define CPUFREQ_MAX CPU_FREQ
+/* TODO: figure out if this does in fact affect power consumption. */
+#define CPUFREQ_DEFAULT (CPUFREQ_MAX/4)
+#define CPUFREQ_NORMAL (CPUFREQ_MAX/4)
+#define HAVE_ADJUSTABLE_CPU_FREQ
+#define HAVE_GUI_BOOST
+
+/* Kernel defines */
+#define INCLUDE_TIMEOUT_API
+#define HAVE_SEMAPHORE_OBJECTS
+
+/* Drivers */
+#define HAVE_I2C_ASYNC
+
+/* Buffer for plugins and codecs. */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
+
+/* LCD defines */
+#define CONFIG_LCD LCD_FIIOM3K
+#define LCD_WIDTH 240
+#define LCD_HEIGHT 320
+#define LCD_DEPTH 16
+#define LCD_PIXELFORMAT RGB565
+#define LCD_DPI 200
+#define HAVE_LCD_COLOR
+#define HAVE_LCD_BITMAP
+#define HAVE_LCD_ENABLE
+#define LCD_X1000_FASTSLEEP
+
+/* Backlight defines */
+#define HAVE_BACKLIGHT
+#define HAVE_BACKLIGHT_BRIGHTNESS
+#define HAVE_BUTTON_LIGHT
+#define HAVE_BUTTONLIGHT_BRIGHTNESS
+#define MIN_BRIGHTNESS_SETTING 1
+#define MAX_BRIGHTNESS_SETTING 100
+#define BRIGHTNESS_STEP 5
+#define DEFAULT_BRIGHTNESS_SETTING 70
+#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
+
+/* Codec / audio hardware defines */
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define HAVE_AK4376
+#define HAVE_SW_TONE_CONTROLS
+#define HAVE_SW_VOLUME_CONTROL
+
+/* TODO: Need to implement recording */
+
+/* Button defines */
+#define CONFIG_KEYPAD FIIO_M3K_PAD
+#define HAVE_HEADPHONE_DETECTION
+#define HAVE_TOUCHPAD
+#define HAVE_TOUCHPAD_SENSITIVITY_SETTING
+#define MIN_TOUCHPAD_SENSITIVITY_SETTING (-25)
+#define MAX_TOUCHPAD_SENSITIVITY_SETTING (25)
+#define DEFAULT_TOUCHPAD_SENSITIVITY_SETTING (0)
+#define HAVE_SCROLLWHEEL
+/* #define HAVE_WHEEL_ACCELERATION */
+/* #define WHEEL_ACCELERATION */
+/* #define WHEEL_ACCEL_START */
+
+/* Storage defines */
+#define CONFIG_STORAGE STORAGE_SD
+#define HAVE_HOTSWAP
+#define HAVE_HOTSWAP_STORAGE_AS_MAIN
+#define HAVE_MULTIDRIVE
+#define NUM_DRIVES 1
+#define STORAGE_WANTS_ALIGN
+#define STORAGE_NEEDS_BOUNCE_BUFFER
+
+/* RTC settings */
+#define CONFIG_RTC RTC_X1000
+/* TODO: implement HAVE_RTC_ALARM */
+
+/* Power management */
+#define HAVE_AXP173
+#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_CHARGING CHARGING_MONITOR
+#define HAVE_SW_POWEROFF
+#define HAVE_POWEROFF_WHILE_CHARGING
+
+/* Only one battery type */
+#define BATTERY_CAPACITY_DEFAULT 1100
+#define BATTERY_CAPACITY_MIN 1100
+#define BATTERY_CAPACITY_MAX 1100
+#define BATTERY_CAPACITY_INC 0
+#define BATTERY_TYPES_COUNT 1
+
+/* USB is still TODO. */
+#define USB_NONE
+
+/* Rockbox capabilities */
+#define HAVE_FAT16SUPPORT
+#define HAVE_ALBUMART
+#define HAVE_BMP_SCALING
+#define HAVE_JPEG
+#define HAVE_TAGCACHE
+#define HAVE_VOLUME_IN_LIST
+#define HAVE_QUICKSCREEN
+#define HAVE_HOTKEY
+#define AB_REPEAT_ENABLE
diff --git a/firmware/export/installer.h b/firmware/export/installer.h
new file mode 100644
index 0000000000..802798618d
--- /dev/null
+++ b/firmware/export/installer.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _INSTALLER_H_
+#define _INSTALLER_H_
+
+/* Provisional interface for installing/dumping a bootloader */
+
+extern int install_bootloader(const char* path);
+extern int dump_bootloader(const char* path);
+extern const char* installer_strerror(int rc);
+
+#endif /* _INSTALLER_H_ */
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index 3b56242b21..a05e0aeb68 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -131,7 +131,8 @@ static int battery_type = 0;
/* Power history: power_history[0] is the newest sample */
unsigned short power_history[POWER_HISTORY_LEN] = {0};
-#if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || (CONFIG_PLATFORM & PLATFORM_HOSTED)
+#if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || \
+ (CONFIG_CPU == X1000) || (CONFIG_PLATFORM & PLATFORM_HOSTED)
static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK];
#else
static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK];
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.c b/firmware/target/mips/ingenic_x1000/aic-x1000.c
new file mode 100644
index 0000000000..a0e509d3b6
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/aic-x1000.c
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "aic-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/aic.h"
+#include "x1000/cpm.h"
+
+/* Given a rational number m/n < 1, find its representation as a continued
+ * fraction [0; a1, a2, a3, ..., a_k]. At most "cnt" terms are calculated
+ * and written out to "buf". Returns the number of terms written; the result
+ * is complete if this value is less than "cnt", and may be incomplete if it
+ * is equal to "cnt". (Note the leading zero term is not written to "buf".)
+ */
+static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt)
+{
+ unsigned wrote = 0;
+ unsigned a = m / n;
+ while(cnt--) {
+ unsigned tmp = n;
+ n = m - n * a;
+ if(n == 0)
+ break;
+
+ m = tmp;
+ a = m / n;
+ *buf++ = a;
+ wrote++;
+ }
+
+ return wrote;
+}
+
+/* Given a finite continued fraction [0; buf[0], buf[1], ..., buf[count-1]],
+ * calculate the rational number m/n which it represents. Returns m and n.
+ * If count is zero, then m and n are undefined.
+ */
+static void cf_expand(const unsigned* buf, unsigned count,
+ unsigned* m, unsigned* n)
+{
+ if(count == 0)
+ return;
+
+ unsigned i = count - 1;
+ unsigned mx = 1, nx = buf[i];
+ while(i--) {
+ unsigned tmp = nx;
+ nx = mx + buf[i] * nx;
+ mx = tmp;
+ }
+
+ *m = mx;
+ *n = nx;
+}
+
+int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult)
+{
+ /* get the input clock rate */
+ uint32_t src_freq = clk_get(clksrc);
+
+ /* reject invalid parameters */
+ if(mult % 64 != 0)
+ return -1;
+
+ if(clksrc == X1000_EXCLK_FREQ) {
+ if(mult != 0)
+ return -1;
+
+ jz_writef(AIC_I2SCR, STPBK(1));
+ jz_writef(CPM_I2SCDR, CS(0), CE(0));
+ REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs;
+ } else {
+ if(mult == 0)
+ return -1;
+ if(fs*mult > src_freq)
+ return -1;
+
+ /* calculate best rational approximation that fits our constraints */
+ unsigned m = 0, n = 0;
+ unsigned buf[16];
+ unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16);
+ do {
+ cf_expand(&buf[0], cnt, &m, &n);
+ cnt -= 1;
+ } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m));
+
+ /* wrong values */
+ if(cnt == 0 || n == 0 || m == 0)
+ return -1;
+
+ jz_writef(AIC_I2SCR, STPBK(1));
+ jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0),
+ CS(1), CE(1), DIV_M(m), DIV_N(n));
+ jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1);
+ REG_AIC_I2SDIV = (mult / 64) - 1;
+ }
+
+ jz_writef(AIC_I2SCR, STPBK(0));
+ return 0;
+}
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.h b/firmware/target/mips/ingenic_x1000/aic-x1000.h
new file mode 100644
index 0000000000..eda0f80f04
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/aic-x1000.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __AIC_X1000_H__
+#define __AIC_X1000_H__
+
+#include "clk-x1000.h"
+#include <stdbool.h>
+
+/* Set frequency of I2S master clock supplied by AIC. Has no use if an
+ * external DAC is supplying the master clock. Must be called with the
+ * bit clock disabled.
+ *
+ * - clksrc can be one of EXCLK, SCLK_A, MPLL.
+ * - This function does not modify PLL settings. It's the caller's job
+ * to ensure the PLL is configured and runing.
+ * - fs is the audio sampling frequency (8 KHz - 192 KHz)
+ * - mult is multiplied by fs to get the master clock rate.
+ * - mult must be a multiple of 64 due to AIC bit clock requirements.
+ * - Note: EXCLK bypasses the decimal divider so it is not very flexible.
+ * If using EXCLK you must set mult=0. If EXCLK is not a multiple of
+ * the bit clock (= 64*fs), then the clock rate will be inaccurate.
+ *
+ * Returns zero on success and nonzero if the frequency is not achievable.
+ */
+extern int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult);
+
+#endif /* __AIC_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/app.lds b/firmware/target/mips/ingenic_x1000/app.lds
new file mode 100644
index 0000000000..0f6352b8ee
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/app.lds
@@ -0,0 +1,119 @@
+#include "config.h"
+
+OUTPUT_FORMAT("elf32-littlemips")
+OUTPUT_ARCH(MIPS)
+ENTRY(_start)
+STARTUP(target/mips/ingenic_x1000/crt0.o)
+
+/* Stub area is used for loading new firmware via RoLo */
+#define STUBSIZE 0x4000
+#define SDRAM_ORIG 0x80000000
+
+/* IRAM contains stub, DRAM contains main app */
+#define IRAMORIG SDRAM_ORIG
+#define IRAMSIZE STUBSIZE
+#define DRAMORIG (SDRAM_ORIG + STUBSIZE)
+#define DRAMSIZE (MEMORYSIZE * 0x100000 - STUBSIZE)
+
+/* End of the audio buffer, where the codec buffer starts */
+#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
+
+/* Where the codec buffer ends, and the plugin buffer starts */
+#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
+
+MEMORY
+{
+ IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
+ DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
+}
+
+SECTIONS
+{
+ .text :
+ {
+ loadaddress = .;
+ _loadaddress = .;
+ *(.init.text);
+ *(.text*);
+ } > DRAM
+
+ . = ALIGN(4);
+ .rodata :
+ {
+ *(.rodata*);
+ } > DRAM
+
+ . = ALIGN(4);
+ .data :
+ {
+ *(.data*);
+ *(.sdata*);
+ } > DRAM
+
+ .iram IRAMORIG: AT (_bssbegin)
+ {
+ _iramstart = .;
+ . = 0x000; /* TLB refill */
+ KEEP(*(.vectors.1));
+ . = 0x100; /* Cache error */
+ KEEP(*(.vectors.2));
+ . = 0x180; /* General exception */
+ KEEP(*(.vectors.3));
+ . = 0x200; /* Interrupt */
+ KEEP(*(.vectors.4));
+ KEEP(*(.vectors));
+
+ *(.icode);
+ *(.irodata);
+ *(.idata);
+ _iramend = .;
+ } > IRAM
+ _iramcopy = LOADADDR(.iram);
+
+ . = ALIGN(4);
+ .stack (NOLOAD) :
+ {
+ *(.stack);
+ stackbegin = .;
+ . += 0x1E00;
+ stackend = .;
+ _irqstackbegin = .;
+ . += 0x300;
+ _irqstackend = .;
+ } > IRAM
+
+ .bss (NOLOAD) :
+ {
+ _bssbegin = .;
+ *(.sbss*);
+ *(.bss*);
+ *(COMMON);
+ *(.scommon*);
+ _bssend = .;
+ _end = .;
+ } > DRAM
+
+#ifdef BOOTLOADER
+ . = ALIGN(4);
+ loadbuffer = .;
+ . += 0x100000 * 4; /* Allow 4 MiB for the rockbox binary */
+ loadbufferend = .;
+#else
+
+ .audiobuf :
+ {
+ . = ALIGN(4); /* XXX might need more alignment here */
+ audiobuffer = .;
+ } > DRAM
+
+ audiobufend = ENDAUDIOADDR;
+ codecbuf = ENDAUDIOADDR;
+ pluginbuf = ENDCODECADDR;
+#endif
+
+ /DISCARD/ :
+ {
+ *(.eh_frame);
+ *(.rel.dyn);
+ }
+}
diff --git a/firmware/target/mips/ingenic_x1000/boot.lds b/firmware/target/mips/ingenic_x1000/boot.lds
new file mode 100644
index 0000000000..81468a95fc
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot.lds
@@ -0,0 +1,5 @@
+#ifdef BOOTLOADER_SPL
+# include "spl.lds"
+#else
+# include "app.lds"
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
new file mode 100644
index 0000000000..390d9722ac
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/msc.h"
+#include "x1000/aic.h"
+
+static uint32_t pll_get(uint32_t pllreg, uint32_t onbit)
+{
+ if((pllreg & (1 << onbit)) == 0)
+ return 0;
+
+ /* Both PLL registers share the same layout of N/M/OD bits.
+ * The max multiplier is 128 and max EXCLK is 26 MHz, so the
+ * multiplication should fit within 32 bits without overflow.
+ */
+ uint32_t rate = X1000_EXCLK_FREQ;
+ rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1;
+ rate /= jz_vreadf(pllreg, CPM_APCR, PLLN) + 1;
+ rate >>= jz_vreadf(pllreg, CPM_APCR, PLLOD);
+ return rate;
+}
+
+static uint32_t sclk_a_get(void)
+{
+ switch(jz_readf(CPM_CCR, SEL_SRC)) {
+ case 1: return X1000_EXCLK_FREQ;
+ case 2: return clk_get(X1000_CLK_APLL);
+ default: return 0;
+ }
+}
+
+static uint32_t ccr_get(uint32_t selbit, uint32_t divbit)
+{
+ uint32_t reg = REG_CPM_CCR;
+ uint32_t sel = (reg >> selbit) & 0x3;
+ uint32_t div = (reg >> divbit) & 0xf;
+
+ switch(sel) {
+ case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
+ case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
+ default: return 0;
+ }
+}
+
+static uint32_t ddr_get(void)
+{
+ uint32_t reg = REG_CPM_DDRCDR;
+ uint32_t div = jz_vreadf(reg, CPM_DDRCDR, CLKDIV);
+
+ switch(jz_vreadf(reg, CPM_DDRCDR, CLKSRC)) {
+ case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
+ case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
+ default: return 0;
+ }
+}
+
+static uint32_t lcd_get(void)
+{
+ if(jz_readf(CPM_CLKGR, LCD))
+ return 0;
+
+ uint32_t reg = REG_CPM_LPCDR;
+ uint32_t rate;
+ switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) {
+ case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
+ case 1: rate = clk_get(X1000_CLK_MPLL); break;
+ default: return 0;
+ }
+
+ rate /= jz_vreadf(reg, CPM_LPCDR, CLKDIV) + 1;
+ return rate;
+}
+
+static uint32_t msc_get(int msc)
+{
+ if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) ||
+ (msc == 1 && jz_readf(CPM_CLKGR, MSC1)))
+ return 0;
+
+ uint32_t reg = REG_CPM_MSC0CDR;
+ uint32_t rate;
+ switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) {
+ case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
+ case 1: rate = clk_get(X1000_CLK_MPLL); break;
+ default: return 0;
+ }
+
+ uint32_t div;
+ if(msc == 0)
+ div = jz_readf(CPM_MSC0CDR, CLKDIV);
+ else
+ div = jz_readf(CPM_MSC1CDR, CLKDIV);
+
+ rate /= 2 * (div + 1);
+ rate >>= REG_MSC_CLKRT(msc);
+ return rate;
+}
+
+static uint32_t i2s_mclk_get(void)
+{
+ if(jz_readf(CPM_CLKGR, AIC))
+ return 0;
+
+ uint32_t reg = REG_CPM_I2SCDR;
+ unsigned long long rate;
+ if(jz_vreadf(reg, CPM_I2SCDR, CS) == 0)
+ rate = X1000_EXCLK_FREQ;
+ else {
+ if(jz_vreadf(reg, CPM_I2SCDR, PCS) == 0)
+ rate = clk_get(X1000_CLK_SCLK_A);
+ else
+ rate = clk_get(X1000_CLK_MPLL);
+
+ rate *= jz_vreadf(reg, CPM_I2SCDR, DIV_M);
+ rate /= jz_vreadf(reg, CPM_I2SCDR, DIV_N);
+ }
+
+ /* Clamp invalid setting to 32 bits */
+ if(rate > 0xffffffffull)
+ rate = 0xffffffff;
+
+ return rate;
+}
+
+static uint32_t i2s_bclk_get(void)
+{
+ return i2s_mclk_get() / (REG_AIC_I2SDIV + 1);
+}
+
+static uint32_t sfc_get(void)
+{
+ if(jz_readf(CPM_CLKGR, SFC))
+ return 0;
+
+ uint32_t reg = REG_CPM_SSICDR;
+ uint32_t rate;
+ if(jz_vreadf(reg, CPM_SSICDR, SFC_CS) == 0)
+ rate = clk_get(X1000_CLK_SCLK_A);
+ else
+ rate = clk_get(X1000_CLK_MPLL);
+
+ rate /= jz_vreadf(reg, CPM_SSICDR, CLKDIV) + 1;
+ return rate;
+}
+
+uint32_t clk_get(x1000_clk_t clk)
+{
+ switch(clk) {
+ case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ;
+ case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON);
+ case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON);
+ case X1000_CLK_SCLK_A: return sclk_a_get();
+ case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV);
+ case X1000_CLK_L2CACHE: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_L2DIV);
+ case X1000_CLK_AHB0: return ccr_get(BP_CPM_CCR_SEL_H0PLL, BP_CPM_CCR_H0DIV);
+ case X1000_CLK_AHB2: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_H2DIV);
+ case X1000_CLK_PCLK: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_PDIV);
+ case X1000_CLK_DDR: return ddr_get();
+ case X1000_CLK_LCD: return lcd_get();
+ case X1000_CLK_MSC0: return msc_get(0);
+ case X1000_CLK_MSC1: return msc_get(1);
+ case X1000_CLK_I2S_MCLK: return i2s_mclk_get();
+ case X1000_CLK_I2S_BCLK: return i2s_bclk_get();
+ case X1000_CLK_SFC: return sfc_get();
+ default: return 0;
+ }
+}
+
+const char* clk_get_name(x1000_clk_t clk)
+{
+ switch(clk) {
+#define CASE(x) case X1000_CLK_##x: return #x
+ CASE(EXCLK);
+ CASE(APLL);
+ CASE(MPLL);
+ CASE(SCLK_A);
+ CASE(CPU);
+ CASE(L2CACHE);
+ CASE(AHB0);
+ CASE(AHB2);
+ CASE(PCLK);
+ CASE(DDR);
+ CASE(LCD);
+ CASE(MSC0);
+ CASE(MSC1);
+ CASE(I2S_MCLK);
+ CASE(I2S_BCLK);
+ CASE(SFC);
+#undef CASE
+ default:
+ return "NONE";
+ }
+}
+
+#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL)
+#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX)
+#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY)
+
+void clk_set_ccr_mux(uint32_t muxbits)
+{
+ /* Set new mux configuration */
+ uint32_t reg = REG_CPM_CCR;
+ reg &= ~CCR_MUX_BITS;
+ reg |= muxbits & CCR_MUX_BITS;
+ REG_CPM_CCR = reg;
+
+ /* Wait for mux change to complete */
+ while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS);
+}
+
+void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk)
+{
+ /* Set new divider configuration */
+ jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1),
+ H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1),
+ CE_CPU(1), CE_AHB0(1), CE_AHB2(1));
+
+ /* Wait until divider change completes */
+ while(REG_CPM_CSR & CSR_DIV_BITS);
+
+ /* Disable CE bits after change */
+ jz_writef(CPM_CCR, CE_CPU(0), CE_AHB0(0), CE_AHB2(0));
+}
+
+void clk_set_ddr(x1000_clk_t src, uint32_t div)
+{
+ /* Write new configuration */
+ jz_writef(CPM_DDRCDR, CE(1), CLKDIV(div - 1),
+ CLKSRC(src == X1000_CLK_MPLL ? 2 : 1));
+
+ /* Wait until mux and divider change are complete */
+ while(jz_readf(CPM_CSR, DDR_MUX) == 0);
+ while(jz_readf(CPM_DDRCDR, BUSY));
+
+ /* Disable CE bit after change */
+ jz_writef(CPM_DDRCDR, CE(0));
+}
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h
new file mode 100644
index 0000000000..76413b90d2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __CLK_X1000_H__
+#define __CLK_X1000_H__
+
+#include <stdint.h>
+#include "x1000/cpm.h"
+
+/* Used as arguments to clk_set_ccr_mux() */
+#define CLKMUX_SCLK_A(x) jz_orf(CPM_CCR, SEL_SRC_V(x))
+#define CLKMUX_CPU(x) jz_orf(CPM_CCR, SEL_CPLL_V(x))
+#define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x))
+#define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x))
+
+typedef enum x1000_clk_t {
+ X1000_CLK_EXCLK,
+ X1000_CLK_APLL,
+ X1000_CLK_MPLL,
+ X1000_CLK_SCLK_A,
+ X1000_CLK_CPU,
+ X1000_CLK_L2CACHE,
+ X1000_CLK_AHB0,
+ X1000_CLK_AHB2,
+ X1000_CLK_PCLK,
+ X1000_CLK_DDR,
+ X1000_CLK_LCD,
+ X1000_CLK_MSC0,
+ X1000_CLK_MSC1,
+ X1000_CLK_I2S_MCLK,
+ X1000_CLK_I2S_BCLK,
+ X1000_CLK_SFC,
+ X1000_CLK_COUNT,
+} x1000_clk_t;
+
+/* Calculate the current frequency of a clock */
+extern uint32_t clk_get(x1000_clk_t clk);
+
+/* Get the name of a clock for debug purposes */
+extern const char* clk_get_name(x1000_clk_t clk);
+
+/* Sets system clock multiplexers */
+extern void clk_set_ccr_mux(uint32_t muxbits);
+
+/* Sets system clock dividers */
+extern void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk);
+
+/* Sets DDR clock source and divider */
+extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
+
+/* Returns the smallest n such that infreq/n <= outfreq */
+inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq)
+{
+ return (infreq + (outfreq - 1)) / outfreq;
+}
+
+/* Returns the smallest n such that (infreq >> n) <= outfreq */
+inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq)
+{
+ uint32_t div = clk_calc_div(infreq, outfreq);
+ return __builtin_clz(div) ^ 31;
+}
+
+#endif /* __CLK_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S
new file mode 100644
index 0000000000..b717f96692
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/crt0.S
@@ -0,0 +1,265 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "mips.h"
+
+ .text
+ .extern main
+ .global _start
+
+ .set push
+ .set mips32
+ .set noreorder
+ .set noat
+
+ .section .init.text
+
+_start:
+ /* Clear data watchpoint */
+ mtc0 zero, C0_WATCHLO
+ mtc0 zero, C0_WATCHHI
+
+ /* Set BEV, ERL, mask interrupts */
+ li v0, 0x40fc04
+ mtc0 v0, C0_Status
+
+ /* Set Cause_IV to 1 (use special interrupt vector) */
+ li v0, M_CauseIV
+ mtc0 v0, C0_Cause
+
+ /* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
+ lui v0, 0xb000
+ lw v1, 0x24(v0)
+ ori v1, v1, 0x22
+ sw v1, 0x24(v0)
+
+ /* Enable kseg0 cacheability */
+ li v0, 3
+ mtc0 v0, C0_Config
+ nop
+
+ /* According to ingenic: "enable idx-store-data cache insn" */
+ li v0, 0x20000000
+ mtc0 v0, C0_ErrCtl
+
+ /* Cache init */
+ li v0, 0x80000000
+ ori v1, v0, 0x4000
+ mtc0 zero, C0_TAGLO
+ mtc0 zero, C0_TAGHI
+_cache_loop:
+ cache ICIndexStTag, 0(v0)
+ cache DCIndexStTag, 0(v0)
+ addiu v0, v0, 32
+ bne v0, v1, _cache_loop
+ nop
+
+ /* Invalidate BTB */
+ mfc0 v0, C0_Config, 7
+ nop
+ ori v0, v0, 2
+ mtc0 v0, C0_Config, 7
+ nop
+
+#ifndef BOOTLOADER_SPL
+ /* Copy IRAM from BSS to low memory. */
+ la t0, _iramcopy
+ la t1, _iramstart
+ la t2, _iramend
+_iram_loop:
+ lw t3, 0(t0)
+ addiu t1, 4
+ addiu t0, 4
+ bne t1, t2, _iram_loop
+ sw t3, -4(t1)
+#endif
+
+ /* Clear the BSS segment (needed to zero-initialize C static values) */
+ la t0, _bssbegin
+ la t1, _bssend
+ beq t0, t1, _bss_done
+_bss_loop:
+ addiu t0, 4
+ bne t0, t1, _bss_loop
+ sw zero, -4(t0)
+_bss_done:
+
+#ifndef BOOTLOADER_SPL
+ /* Set stack pointer and clear the stack */
+ la sp, stackend
+ la t0, stackbegin
+ li t1, 0xDEADBEEF
+_stack_loop:
+ addiu t0, 4
+ bne t0, sp, _stack_loop
+ sw t1, -4(t0)
+
+ /* Clear the IRQ stack */
+ la k0, _irqstackend
+ la t0, _irqstackbegin
+_irqstack_loop:
+ addiu t0, 4
+ bne t0, k0, _irqstack_loop
+ sw t1, -4(t0)
+#endif
+
+ /* Jump to C code */
+ j main
+ nop
+
+#ifndef BOOTLOADER_SPL
+ /* Exception entry points */
+ .section .vectors.1, "ax", %progbits
+ j tlb_refill_handler
+ nop
+
+ .section .vectors.2, "ax", %progbits
+ j real_exception_handler
+ nop
+
+ .section .vectors.3, "ax", %progbits
+ j real_exception_handler
+ nop
+
+ .section .vectors.4, "ax", %progbits
+ j real_exception_handler
+ nop
+
+ .section .vectors, "ax", %progbits
+real_exception_handler:
+ move k0, sp
+ la sp, _irqstackend
+ addiu sp, -0x84
+ sw k0, 0x80(sp)
+ sw ra, 0x00(sp)
+ sw fp, 0x04(sp)
+ sw gp, 0x08(sp)
+ sw t9, 0x0c(sp)
+ sw t8, 0x10(sp)
+ sw s7, 0x14(sp)
+ sw s6, 0x18(sp)
+ sw s5, 0x1c(sp)
+ sw s4, 0x20(sp)
+ sw s3, 0x24(sp)
+ sw s2, 0x28(sp)
+ sw s1, 0x2c(sp)
+ sw s0, 0x30(sp)
+ sw t7, 0x34(sp)
+ sw t6, 0x38(sp)
+ sw t5, 0x3c(sp)
+ sw t4, 0x40(sp)
+ sw t3, 0x44(sp)
+ sw t2, 0x48(sp)
+ sw t1, 0x4c(sp)
+ sw t0, 0x50(sp)
+ sw a3, 0x54(sp)
+ sw a2, 0x58(sp)
+ sw a1, 0x5c(sp)
+ sw a0, 0x60(sp)
+ sw v1, 0x64(sp)
+ sw v0, 0x68(sp)
+ sw $1, 0x6c(sp)
+ mflo k0
+ nop
+ sw k0, 0x70(sp)
+ mfhi k0
+ nop
+ sw k0, 0x74(sp)
+ mfc0 k0, C0_STATUS
+ nop
+ nop
+ nop
+ sw k0, 0x78(sp)
+ mfc0 k0, C0_EPC
+ nop
+ nop
+ nop
+ sw k0, 0x7c(sp)
+
+ li k1, M_CauseExcCode
+ mfc0 a0, C0_CAUSE
+ and k0, a0, k1
+ bnez k0, _exception
+ nop
+ jal intr_handler
+ nop
+ j _exception_return
+
+_exception:
+ mfc0 a1, C0_EPC
+ nop
+ nop
+ nop
+ jal exception_handler
+ move a2, sp
+
+_exception_return:
+ lw ra, 0x00(sp)
+ lw fp, 0x04(sp)
+ lw gp, 0x08(sp)
+ lw t9, 0x0c(sp)
+ lw t8, 0x10(sp)
+ lw s7, 0x14(sp)
+ lw s6, 0x18(sp)
+ lw s5, 0x1c(sp)
+ lw s4, 0x20(sp)
+ lw s3, 0x24(sp)
+ lw s2, 0x28(sp)
+ lw s1, 0x2c(sp)
+ lw s0, 0x30(sp)
+ lw t7, 0x34(sp)
+ lw t6, 0x38(sp)
+ lw t5, 0x3c(sp)
+ lw t4, 0x40(sp)
+ lw t3, 0x44(sp)
+ lw t2, 0x48(sp)
+ lw t1, 0x4c(sp)
+ lw t0, 0x50(sp)
+ lw a3, 0x54(sp)
+ lw a2, 0x58(sp)
+ lw a1, 0x5c(sp)
+ lw a0, 0x60(sp)
+ lw v1, 0x64(sp)
+ lw v0, 0x68(sp)
+ lw $1, 0x6c(sp)
+ lw k0, 0x70(sp)
+ mtlo k0
+ nop
+ lw k0, 0x74(sp)
+ mthi k0
+ nop
+ lw k0, 0x78(sp)
+ mtc0 k0, C0_STATUS
+ nop
+ nop
+ nop
+ lw k0, 0x7c(sp)
+ mtc0 k0, C0_EPC
+ nop
+ nop
+ nop
+ lw sp, 0x80(sp)
+ eret
+ nop
+#endif
+
+ .set pop
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
new file mode 100644
index 0000000000..fed586691c
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -0,0 +1,215 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef BOOTLOADER
+#include "system.h"
+#include "kernel.h"
+#include "button.h"
+#include "lcd.h"
+#include "font.h"
+#include "action.h"
+#include "list.h"
+
+#include "clk-x1000.h"
+#include "gpio-x1000.h"
+
+static bool dbg_clocks(void)
+{
+ do {
+ lcd_clear_display();
+ int line = 0;
+ for(int i = 0; i < X1000_CLK_COUNT; ++i) {
+ uint32_t hz = clk_get(i);
+ uint32_t khz = hz / 1000;
+ uint32_t mhz = khz / 1000;
+ lcd_putsf(2, line++, "%8s %4u,%03u,%03u Hz", clk_get_name(i),
+ mhz, (khz - mhz*1000), (hz - khz*1000));
+ }
+
+ lcd_update();
+ } while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
+
+ return false;
+}
+
+static void dbg_gpios_show_state(void)
+{
+ const char portname[] = "ABCD";
+ for(int i = 0; i < 4; ++i)
+ lcd_putsf(0, i, "GPIO %c: %08x", portname[i], REG_GPIO_PIN(i));
+}
+
+static void dbg_gpios_show_config(void)
+{
+ const char portname[] = "ABCD";
+ int line = 0;
+ for(int i = 0; i < 4; ++i) {
+ uint32_t intr = REG_GPIO_INT(i);
+ uint32_t mask = REG_GPIO_MSK(i);
+ uint32_t pat0 = REG_GPIO_PAT0(i);
+ uint32_t pat1 = REG_GPIO_PAT1(i);
+ lcd_putsf(0, line++, "GPIO %c", portname[i]);
+ lcd_putsf(2, line++, " int %08lx", intr);
+ lcd_putsf(2, line++, " msk %08lx", mask);
+ lcd_putsf(2, line++, "pat0 %08lx", pat0);
+ lcd_putsf(2, line++, "pat1 %08lx", pat1);
+ line++;
+ }
+}
+
+static bool dbg_gpios(void)
+{
+ enum { STATE, CONFIG, NUM_SCREENS };
+ const int timeouts[NUM_SCREENS] = { 1, HZ };
+ int screen = STATE;
+
+ while(1) {
+ lcd_clear_display();
+ switch(screen) {
+ case CONFIG:
+ dbg_gpios_show_config();
+ break;
+ case STATE:
+ dbg_gpios_show_state();
+ break;
+ }
+
+ lcd_update();
+
+ switch(get_action(CONTEXT_STD, timeouts[screen])) {
+ case ACTION_STD_CANCEL:
+ return false;
+ case ACTION_STD_PREV:
+ case ACTION_STD_PREVREPEAT:
+ screen -= 1;
+ if(screen < 0)
+ screen = NUM_SCREENS - 1;
+ break;
+ case ACTION_STD_NEXT:
+ case ACTION_STD_NEXTREPEAT:
+ screen += 1;
+ if(screen >= NUM_SCREENS)
+ screen = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+extern volatile unsigned aic_tx_underruns;
+
+static bool dbg_audio(void)
+{
+ do {
+ lcd_clear_display();
+ lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
+ lcd_update();
+ } while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
+
+ return false;
+}
+
+static bool dbg_cpuidle(void)
+{
+ do {
+ lcd_clear_display();
+ lcd_putsf(0, 0, "CPU idle time: %d.%01d%%",
+ __cpu_idle_cur/10, __cpu_idle_cur%10);
+ lcd_putsf(0, 1, "CPU frequency: %d.%03d MHz",
+ FREQ/1000000, (FREQ%1000000)/1000);
+ lcd_update();
+ } while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
+
+ return false;
+}
+
+#ifdef FIIO_M3K
+extern bool dbg_fiiom3k_touchpad(void);
+extern bool axp173_debug_menu(void);
+#endif
+
+/* Menu definition */
+static const struct {
+ const char* name;
+ bool(*function)(void);
+} menuitems[] = {
+ {"Clocks", &dbg_clocks},
+ {"GPIOs", &dbg_gpios},
+ {"CPU idle", &dbg_cpuidle},
+ {"Audio", &dbg_audio},
+#ifdef FIIO_M3K
+ {"Touchpad", &dbg_fiiom3k_touchpad},
+ {"Power stats", &axp173_debug_menu},
+#endif
+};
+
+static int hw_info_menu_action_cb(int btn, struct gui_synclist* lists)
+{
+ if(btn == ACTION_STD_OK) {
+ int sel = gui_synclist_get_sel_pos(lists);
+ FOR_NB_SCREENS(i)
+ viewportmanager_theme_enable(i, false, NULL);
+
+ lcd_setfont(FONT_SYSFIXED);
+ lcd_set_foreground(LCD_WHITE);
+ lcd_set_background(LCD_BLACK);
+
+ if(menuitems[sel].function())
+ btn = SYS_USB_CONNECTED;
+ else
+ btn = ACTION_REDRAW;
+
+ lcd_setfont(FONT_UI);
+
+ FOR_NB_SCREENS(i)
+ viewportmanager_theme_undo(i, false);
+ }
+
+ return btn;
+}
+
+static const char* hw_info_menu_get_name(int item, void* data,
+ char* buffer, size_t buffer_len)
+{
+ (void)buffer;
+ (void)buffer_len;
+ (void)data;
+ return menuitems[item].name;
+}
+
+bool dbg_hw_info(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, MODEL_NAME " debug menu",
+ ARRAYLEN(menuitems), NULL);
+ info.action_callback = hw_info_menu_action_cb;
+ info.get_name = hw_info_menu_get_name;
+ return simplelist_show_list(&info);
+}
+
+bool dbg_ports(void)
+{
+ return false;
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/dma-x1000.c b/firmware/target/mips/ingenic_x1000/dma-x1000.c
new file mode 100644
index 0000000000..28fd328a85
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/dma-x1000.c
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "dma-x1000.h"
+#include "irq-x1000.h"
+#include "x1000/cpm.h"
+#include "panic.h"
+
+static dma_cb_func dma_callbacks[DMA_NUM_USED_CHANNELS];
+
+static void dma_no_cb(int event)
+{
+ (void)event;
+ panicf("Unhandled DMA channel interrupt");
+}
+
+void dma_init(void)
+{
+ for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i)
+ dma_callbacks[i] = dma_no_cb;
+
+ jz_writef(CPM_CLKGR, PDMA(0));
+ jz_writef(DMA_CTRL, ENABLE(1), HALT(0), AR(0));
+ jz_writef(DMA_CTRL, FMSC(1), FSSI(1), FTSSI(1), FUART(1), FAIC(1));
+ system_enable_irq(IRQ_PDMA);
+ system_enable_irq(IRQ_PDMAD);
+}
+
+void dma_set_callback(int chn, dma_cb_func cb)
+{
+ dma_callbacks[chn] = cb != NULL ? cb : dma_no_cb;
+}
+
+void PDMA(void)
+{
+ /* This is called when the last descriptor completes, or if the
+ * channel hits an error.
+ */
+ unsigned pending = REG_DMA_IRQP;
+ for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i) {
+ if((pending & (1 << i)) == 0)
+ continue;
+
+ int evt;
+ if(REG_DMA_CHN_CS(i) & jz_orm(DMA_CHN_CS, AR, HLT))
+ evt = DMA_EVENT_ERROR;
+ else
+ evt = DMA_EVENT_COMPLETE;
+
+ REG_DMA_CHN_CS(i) = 0;
+ dma_callbacks[i](evt);
+ }
+
+ /* Clear any errors and clear interrupts */
+ jz_writef(DMA_CTRL, HALT(0), AR(0));
+ REG_DMA_IRQP = 0;
+}
+
+void PDMAD(void)
+{
+ /* Called when TIE is set on a non-final descriptor */
+ unsigned pending = REG_DMA_DIP;
+ for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i) {
+ if((pending & (1 << i)) == 0)
+ continue;
+
+ dma_callbacks[i](DMA_EVENT_INTERRUPT);
+ }
+
+ /* This does not operate like other clear registers */
+ REG_DMA_DIC &= ~pending;
+}
diff --git a/firmware/target/mips/ingenic_x1000/dma-x1000.h b/firmware/target/mips/ingenic_x1000/dma-x1000.h
new file mode 100644
index 0000000000..d836a0cf54
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/dma-x1000.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __DMA_X1000_H__
+#define __DMA_X1000_H__
+
+#include "x1000/dma.h"
+#include "x1000/dma_chn.h"
+#include <stdint.h>
+
+/* Events passed to DMA callbacks */
+#define DMA_EVENT_NONE 0 /* Not used by DMA code but can be used as
+ * a sentinel value to indicate "no event" */
+#define DMA_EVENT_INTERRUPT 1 /* Interrupt on a non-final descriptor */
+#define DMA_EVENT_COMPLETE 2 /* Completed the final descriptor */
+#define DMA_EVENT_ERROR 3 /* Some kind of error occurred */
+
+/* All DMA channels which use interrupts must be statically defined here.
+ * The channel numbering should be contiguous, and lower channel numbers
+ * will have lower interrupt latency because they're serviced first.
+ *
+ * Channels >= DMA_NUM_USED_CHANNELS will NOT have interrupts serviced!
+ * Due to the possibility of address error interrupts that can occur even
+ * if no interrupts are requested on the channel, the unallocated channels
+ * cannot be used safely.
+ */
+#define DMA_CHANNEL_AUDIO 0
+#define DMA_CHANNEL_FBCOPY 1
+#define DMA_NUM_USED_CHANNELS 2
+
+struct dma_desc {
+ uint32_t cm; /* meaning and layout same as DMA_CHN_CM */
+ uint32_t sa; /* source address */
+ uint32_t ta; /* target address */
+ uint32_t tc; /* low 24 bits: transfer count
+ * upper 8 bits: offset to next descriptor
+ */
+ uint32_t sd; /* same as DMA_CHN_SD */
+ uint32_t rt; /* request type, same as DMA_CHN_RT */
+ uint32_t pad0;
+ uint32_t pad1;
+} __attribute__((aligned(32)));
+
+typedef struct dma_desc dma_desc;
+
+typedef void(*dma_cb_func)(int event);
+
+extern void dma_init(void);
+extern void dma_set_callback(int chn, dma_cb_func cb);
+
+#endif /* __DMA_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
new file mode 100644
index 0000000000..2f43809523
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "audiohw.h"
+#include "system.h"
+#include "pcm_sampr.h"
+#include "logf.h"
+#include "aic-x1000.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/aic.h"
+#include "x1000/cpm.h"
+
+void audiohw_init(void)
+{
+ /* Configure AIC for I2S operation */
+ jz_writef(CPM_CLKGR, AIC(0));
+ gpio_config(GPIO_B, 0x1f, GPIO_DEVICE(1));
+ jz_writef(AIC_I2SCR, STPBK(1));
+
+ /* Operate as I2S master, use external codec */
+ jz_writef(AIC_CFG, AUSEL(1), ICDC(0), BCKD(1), SYNCD(1), LSMP(1));
+ jz_writef(AIC_I2SCR, ESCLK(1), AMSL(0));
+
+ /* Stereo audio, packed 16 bit samples */
+ jz_writef(AIC_CCR, PACK16(1), CHANNEL(1), OSS(1));
+
+ /* Initialize DAC */
+ i2c_x1000_set_freq(AK4376_BUS, I2C_FREQ_400K);
+ ak4376_init();
+}
+
+void audiohw_postinit(void)
+{
+}
+
+void audiohw_close(void)
+{
+ ak4376_close();
+}
+
+void ak4376_set_pdn_pin(int level)
+{
+ gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
+}
+
+int ak4376_set_mclk_freq(int hw_freq, bool enabled)
+{
+ /* Get the multiplier */
+ int freq = hw_freq_sampr[hw_freq];
+ int mult = freq >= SAMPR_176 ? 128 : 256;
+
+ if(enabled) {
+ /* Set the new frequency; clock is enabled afterward */
+ if(aic_i2s_set_mclk(X1000_CLK_SCLK_A, freq, mult))
+ logf("WARNING: unachievable audio rate %d x %d!?", freq, mult);
+ } else {
+ /* Shut off the clock */
+ jz_writef(AIC_I2SCR, STPBK(1));
+ }
+
+ return mult;
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
new file mode 100644
index 0000000000..f02fcaaee8
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "backlight.h"
+#include "backlight-target.h"
+#include "lcd.h"
+#include "pwm-x1000.h"
+
+#define BL_LCD_CHN 0
+#define BL_LCD_PERIOD 33000
+
+#define BL_BTN_CHN 4
+#define BL_BTN_PERIOD 100000
+
+static int backlight_calc_duty(int period, int min_duty, int brightness)
+{
+ return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING;
+}
+
+bool backlight_hw_init(void)
+{
+ pwm_init(BL_LCD_CHN);
+ pwm_init(BL_BTN_CHN);
+ pwm_enable(BL_LCD_CHN);
+ pwm_enable(BL_BTN_CHN);
+ backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
+ buttonlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
+ /* TODO: avoid buttonlight flicker when powering up the machine */
+ return true;
+}
+
+void backlight_hw_on(void)
+{
+ pwm_enable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(true);
+#endif
+}
+
+void backlight_hw_off(void)
+{
+ pwm_disable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(false);
+#endif
+}
+
+void backlight_hw_brightness(int brightness)
+{
+ int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness);
+ pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns);
+}
+
+void buttonlight_hw_on(void)
+{
+ pwm_enable(BL_BTN_CHN);
+}
+
+void buttonlight_hw_off(void)
+{
+ pwm_disable(BL_BTN_CHN);
+}
+
+void buttonlight_hw_brightness(int brightness)
+{
+ /* Duty cycle below 11% seems to turn the buttonlight off entirely,
+ * so we need to rescale the range */
+ int duty_ns = backlight_calc_duty(BL_BTN_PERIOD, BL_BTN_PERIOD*11/100, brightness);
+ pwm_set_period(BL_BTN_CHN, BL_BTN_PERIOD, duty_ns);
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h
new file mode 100644
index 0000000000..791a013c32
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __BACKLIGHT_TARGET_H__
+#define __BACKLIGHT_TARGET_H__
+
+#include <stdbool.h>
+
+extern bool backlight_hw_init(void);
+
+extern void backlight_hw_on(void);
+extern void backlight_hw_off(void);
+extern void backlight_hw_brightness(int brightness);
+
+extern void buttonlight_hw_on(void);
+extern void buttonlight_hw_off(void);
+extern void buttonlight_hw_brightness(int brightness);
+
+#endif /* __BACKLIGHT_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
new file mode 100644
index 0000000000..db5ece10b0
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
@@ -0,0 +1,503 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "button.h"
+#include "kernel.h"
+#include "backlight.h"
+#include "panic.h"
+#include "lcd.h"
+#include "gpio-x1000.h"
+#include "i2c-x1000.h"
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef BOOTLOADER
+# include "font.h"
+#endif
+
+#define FT_RST_PIN (1 << 15)
+#define FT_INT_PIN (1 << 12)
+#define ft_interrupt GPIOB12
+
+/* Touch event types */
+#define EVENT_NONE (-1)
+#define EVENT_PRESS 0
+#define EVENT_RELEASE 1
+#define EVENT_CONTACT 2
+
+/* FSM states */
+#define STATE_IDLE 0
+#define STATE_PRESS 1
+#define STATE_REPORT 2
+#define STATE_SCROLL_PRESS 3
+#define STATE_SCROLLING 4
+
+/* Assume there's no active touch if no event is reported in this time */
+#define AUTORELEASE_TIME (10000 * OST_TICKS_PER_US)
+
+/* If there's no significant motion on the scrollbar for this time,
+ * then report it as a button press instead */
+#define SCROLL_PRESS_TIME (100000 * OST_TICKS_PER_US)
+
+/* If a press on the scrollbar moves more than this during SCROLL_PRESS_TIME,
+ * then we enter scrolling mode. */
+#define MIN_SCROLL_THRESH 15
+
+/* If OST tick a is after OST tick b, then returns the number of ticks
+ * in the interval between a and b; otherwise undefined. */
+#define TICKS_SINCE(a, b) ((a) - (b))
+
+/* Number of touch samples to smooth before reading */
+#define TOUCH_SAMPLES 3
+
+static struct ft_driver {
+ int i2c_cookie;
+ i2c_descriptor i2c_desc;
+ uint8_t raw_data[6];
+ bool active;
+
+ /* Number of pixels squared which must be moved before
+ * a scrollbar pulse is generated */
+ int scroll_thresh_sqr;
+} ftd;
+
+static struct ft_state_machine {
+ /* Current button state, used by button_read_device() */
+ int buttons;
+
+ /* FSM state */
+ int state;
+
+ /* Time of the last touch event, as 32-bit OST timestamp. The kernel
+ * tick is simply too low-resolution to work reliably, especially as
+ * we handle touchpad events asynchronously. */
+ uint32_t last_event_t;
+
+ /* Time of entering the SCROLL_PRESS state, used to differentiate
+ * between a press, hold, or scrolling motion */
+ uint32_t scroll_press_t;
+
+ /* Number of CONTACT events sampled in the PRESS state.
+ * Must reach TOUCH_SAMPLES before we move forward. */
+ int samples;
+
+ /* Filter for smoothing touch points */
+ int sum_x, sum_y;
+
+ /* Position of the original touch */
+ int orig_x, orig_y;
+
+ /* Current touch position */
+ int cur_x, cur_y;
+} fsm;
+
+static int touch_to_button(int x, int y)
+{
+ if(x == 900) {
+ /* Right strip */
+ if(y == 80)
+ return BUTTON_BACK;
+ else if(y == 240)
+ return BUTTON_RIGHT;
+ else
+ return 0;
+ } else if(x < 80) {
+ /* Left strip */
+ if(y < 80)
+ return BUTTON_MENU;
+ else if(y > 190)
+ return BUTTON_LEFT;
+ else
+ return 0;
+ } else {
+ /* Middle strip */
+ if(y < 100)
+ return BUTTON_UP;
+ else if(y > 220)
+ return BUTTON_DOWN;
+ else
+ return BUTTON_SELECT;
+ }
+}
+
+static bool ft_accum_touch(uint32_t t, int tx, int ty)
+{
+ /* Record event time */
+ fsm.last_event_t = t;
+
+ if(fsm.samples < TOUCH_SAMPLES) {
+ /* Continue "priming" the filter */
+ fsm.sum_x += tx;
+ fsm.sum_y += ty;
+ fsm.samples += 1;
+
+ /* Return if filter is not ready */
+ if(fsm.samples < TOUCH_SAMPLES)
+ return false;
+ } else {
+ /* Update filter */
+ fsm.sum_x += tx - fsm.sum_x / TOUCH_SAMPLES;
+ fsm.sum_y += ty - fsm.sum_y / TOUCH_SAMPLES;
+ }
+
+ /* Filter is ready, so read the point */
+ fsm.cur_x = fsm.sum_x / TOUCH_SAMPLES;
+ fsm.cur_y = fsm.sum_y / TOUCH_SAMPLES;
+ return true;
+}
+
+static void ft_go_idle(void)
+{
+ /* Null out the touch state */
+ fsm.buttons = 0;
+ fsm.samples = 0;
+ fsm.sum_x = fsm.sum_y = 0;
+ fsm.state = STATE_IDLE;
+}
+
+static void ft_start_report(void)
+{
+ /* Report the button bit */
+ fsm.buttons = touch_to_button(fsm.cur_x, fsm.cur_y);
+ fsm.orig_x = fsm.cur_x;
+ fsm.orig_y = fsm.cur_y;
+ fsm.state = STATE_REPORT;
+}
+
+static void ft_start_report_or_scroll(void)
+{
+ ft_start_report();
+
+ /* If the press occurs on the scrollbar, then we need to
+ * wait an additional delay before reporting it in case
+ * this is the beginning of a scrolling motion */
+ if(fsm.buttons & (BUTTON_UP|BUTTON_DOWN|BUTTON_SELECT)) {
+ fsm.buttons = 0;
+ fsm.scroll_press_t = __ost_read32();
+ fsm.state = STATE_SCROLL_PRESS;
+ }
+}
+
+static void ft_step_state(uint32_t t, int evt, int tx, int ty)
+{
+ /* Generate a release event automatically in case we missed it */
+ if(evt == EVENT_NONE) {
+ if(TICKS_SINCE(t, fsm.last_event_t) >= AUTORELEASE_TIME) {
+ evt = EVENT_RELEASE;
+ tx = fsm.cur_x;
+ ty = fsm.cur_y;
+ }
+ }
+
+ switch(fsm.state) {
+ case STATE_IDLE: {
+ if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
+ /* Move to REPORT or PRESS state */
+ if(ft_accum_touch(t, tx, ty))
+ ft_start_report_or_scroll();
+ else
+ fsm.state = STATE_PRESS;
+ }
+ } break;
+
+ case STATE_PRESS: {
+ if(evt == EVENT_RELEASE) {
+ /* Ignore if the number of samples is too low */
+ ft_go_idle();
+ } else if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
+ /* Accumulate the touch position in the filter */
+ if(ft_accum_touch(t, tx, ty))
+ ft_start_report_or_scroll();
+ }
+ } break;
+
+ case STATE_REPORT: {
+ if(evt == EVENT_RELEASE)
+ ft_go_idle();
+ else if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ ft_accum_touch(t, tx, ty);
+ } break;
+
+ case STATE_SCROLL_PRESS: {
+ if(evt == EVENT_RELEASE) {
+ /* This _should_ synthesize a button press.
+ *
+ * - ft_start_report() will set the button bit based on the
+ * current touch position and enter the REPORT state, which
+ * will automatically hold the bit high
+ *
+ * - The next button_read_device() will see the button bit
+ * and report it back to Rockbox, then step the FSM with
+ * EVENT_NONE.
+ *
+ * - The EVENT_NONE stepping will eventually autogenerate a
+ * RELEASE event and restore the button state back to 0
+ *
+ * - There's a small logic hole in the REPORT state which
+ * could cause it to miss an immediately repeated PRESS
+ * that occurs before the autorelease timeout kicks in.
+ * FIXME: We might want to special-case that.
+ */
+ ft_start_report();
+ break;
+ }
+
+ if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ ft_accum_touch(t, tx, ty);
+
+ int dx = fsm.cur_x - fsm.orig_x;
+ int dy = fsm.cur_y - fsm.orig_y;
+ int dp = (dx*dx) + (dy*dy);
+ if(dp >= MIN_SCROLL_THRESH*MIN_SCROLL_THRESH) {
+ /* Significant motion: enter SCROLLING state */
+ fsm.state = STATE_SCROLLING;
+ } else if(TICKS_SINCE(t, fsm.scroll_press_t) >= SCROLL_PRESS_TIME) {
+ /* No significant motion: report it as a press */
+ fsm.cur_x = fsm.orig_x;
+ fsm.cur_y = fsm.orig_y;
+ ft_start_report();
+ }
+ } break;
+
+ case STATE_SCROLLING: {
+ if(evt == EVENT_RELEASE) {
+ ft_go_idle();
+ break;
+ }
+
+ if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ ft_accum_touch(t, tx, ty);
+
+ int dx = fsm.cur_x - fsm.orig_x;
+ int dy = fsm.cur_y - fsm.orig_y;
+ int dp = (dx*dx) + (dy*dy);
+ if(dp >= ftd.scroll_thresh_sqr) {
+ if(dy < 0) {
+ queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
+ } else {
+ queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
+ }
+
+ /* Poke the backlight */
+ backlight_on();
+ buttonlight_on();
+
+ fsm.orig_x = fsm.cur_x;
+ fsm.orig_y = fsm.cur_y;
+ }
+ } break;
+
+ default:
+ panicf("ft6x06: unhandled state");
+ break;
+ }
+}
+
+static void ft_i2c_callback(int status, i2c_descriptor* desc)
+{
+ (void)desc;
+ if(status != I2C_STATUS_OK)
+ return;
+
+ /* The panel is oriented such that its X axis is vertical,
+ * so swap the axes for reporting */
+ int evt = ftd.raw_data[1] >> 6;
+ int ty = ftd.raw_data[2] | ((ftd.raw_data[1] & 0xf) << 8);
+ int tx = ftd.raw_data[4] | ((ftd.raw_data[3] & 0xf) << 8);
+
+ /* TODO: convert the touch positions to linear positions.
+ *
+ * Points reported by the touch controller are distorted and non-linear,
+ * ideally we'd like to correct these values. There's more precision in
+ * the middle of the touchpad than on the edges, so scrolling feels slow
+ * in the middle and faster near the edge.
+ */
+
+ ft_step_state(__ost_read32(), evt, tx, ty);
+}
+
+void ft_interrupt(void)
+{
+ /* We don't care if this fails */
+ i2c_async_queue(FT6x06_BUS, TIMEOUT_NOBLOCK, I2C_Q_ONCE,
+ ftd.i2c_cookie, &ftd.i2c_desc);
+}
+
+static void ft_init(void)
+{
+ /* Initialize the driver state */
+ ftd.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1);
+ ftd.i2c_desc.slave_addr = FT6x06_ADDR;
+ ftd.i2c_desc.bus_cond = I2C_START | I2C_STOP;
+ ftd.i2c_desc.tran_mode = I2C_READ;
+ ftd.i2c_desc.buffer[0] = &ftd.raw_data[5];
+ ftd.i2c_desc.count[0] = 1;
+ ftd.i2c_desc.buffer[1] = &ftd.raw_data[0];
+ ftd.i2c_desc.count[1] = 5;
+ ftd.i2c_desc.callback = ft_i2c_callback;
+ ftd.i2c_desc.arg = 0;
+ ftd.i2c_desc.next = NULL;
+ ftd.raw_data[5] = 0x02;
+ ftd.active = true;
+ touchpad_set_sensitivity(DEFAULT_TOUCHPAD_SENSITIVITY_SETTING);
+
+ /* Initialize the state machine */
+ fsm.buttons = 0;
+ fsm.state = STATE_IDLE;
+ fsm.last_event_t = 0;
+ fsm.scroll_press_t = 0;
+ fsm.samples = 0;
+ fsm.sum_x = fsm.sum_y = 0;
+ fsm.orig_x = fsm.orig_y = 0;
+ fsm.cur_x = fsm.cur_y = 0;
+
+ /* Bring up I2C bus */
+ i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
+
+ /* Reset chip */
+ gpio_config(GPIO_B, FT_RST_PIN|FT_INT_PIN, GPIO_OUTPUT(0));
+ mdelay(5);
+ gpio_out_level(GPIO_B, FT_RST_PIN, 1);
+ gpio_config(GPIO_B, FT_INT_PIN, GPIO_IRQ_EDGE(0));
+ gpio_enable_irq(GPIO_B, FT_INT_PIN);
+}
+
+void touchpad_set_sensitivity(int level)
+{
+ int pixels = 40;
+ pixels -= level;
+ ftd.scroll_thresh_sqr = pixels * pixels;
+}
+
+void touchpad_enable_device(bool en)
+{
+ i2c_reg_write1(FT6x06_BUS, FT6x06_ADDR, 0xa5, en ? 0 : 3);
+ ftd.active = en;
+}
+
+/* Value of headphone detect register */
+static uint8_t hp_detect_reg = 0x00;
+
+/* Interval to poll the register */
+#define HPD_POLL_TIME (HZ/2)
+
+static int hp_detect_tmo_cb(struct timeout* tmo)
+{
+ i2c_descriptor* d = (i2c_descriptor*)tmo->data;
+ i2c_async_queue(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
+ return HPD_POLL_TIME;
+}
+
+static void hp_detect_init(void)
+{
+ static struct timeout tmo;
+ static const uint8_t gpio_reg = 0x94;
+ static i2c_descriptor desc = {
+ .slave_addr = AXP173_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_READ,
+ .buffer[0] = (void*)&gpio_reg,
+ .count[0] = 1,
+ .buffer[1] = &hp_detect_reg,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+ };
+
+ /* Headphone detect is wired to an undocumented GPIO on the AXP173.
+ * This sets it to input mode so we can see the pin state. */
+ i2c_reg_write1(AXP173_BUS, AXP173_ADDR, 0x93, 0x01);
+
+ /* Get an initial reading before startup */
+ int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, gpio_reg);
+ if(r >= 0)
+ hp_detect_reg = r;
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+}
+
+/* Rockbox interface */
+void button_init_device(void)
+{
+ /* Configure physical button GPIOs */
+ gpio_config(GPIO_A, (1 << 17) | (1 << 19), GPIO_INPUT);
+ gpio_config(GPIO_B, (1 << 28) | (1 << 31), GPIO_INPUT);
+
+ /* Initialize touchpad */
+ ft_init();
+
+ /* Set up headphone detect polling */
+ hp_detect_init();
+}
+
+int button_read_device(void)
+{
+ int r = fsm.buttons;
+ ft_step_state(__ost_read32(), EVENT_NONE, 0, 0);
+
+ /* Read GPIOs for physical buttons */
+ uint32_t a = REG_GPIO_PIN(GPIO_A);
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+
+ /* All buttons are active low */
+ if((a & (1 << 17)) == 0) r |= BUTTON_PLAY;
+ if((a & (1 << 19)) == 0) r |= BUTTON_VOL_UP;
+ if((b & (1 << 28)) == 0) r |= BUTTON_VOL_DOWN;
+ if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
+
+ return r;
+}
+
+bool headphones_inserted()
+{
+ return hp_detect_reg & 0x40 ? true : false;
+}
+
+#ifndef BOOTLOADER
+static int getbtn(void)
+{
+ int btn;
+ do {
+ btn = button_get_w_tmo(1);
+ } while(btn & (BUTTON_REL|BUTTON_REPEAT));
+ return btn;
+}
+
+bool dbg_fiiom3k_touchpad(void)
+{
+ static const char* fsm_statenames[] = {
+ "IDLE", "PRESS", "REPORT", "SCROLL_PRESS", "SCROLLING"
+ };
+
+ do {
+ int line = 0;
+ lcd_clear_display();
+ lcd_putsf(0, line++, "state: %s", fsm_statenames[fsm.state]);
+ lcd_putsf(0, line++, "button: %08x", fsm.buttons);
+ lcd_putsf(0, line++, "pos x: %4d orig x: %4d", fsm.cur_x, fsm.orig_x);
+ lcd_putsf(0, line++, "pos y: %4d orig y: %4d", fsm.cur_y, fsm.orig_y);
+ lcd_update();
+ } while(getbtn() != BUTTON_POWER);
+ return false;
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h
new file mode 100644
index 0000000000..f75a43242d
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __BUTTON_TARGET_H__
+#define __BUTTON_TARGET_H__
+
+#include <stdbool.h>
+
+#define BUTTON_POWER 0x00000001
+#define BUTTON_PLAY 0x00000002
+#define BUTTON_VOL_UP 0x00000004
+#define BUTTON_VOL_DOWN 0x00000008
+#define BUTTON_UP 0x00000010
+#define BUTTON_DOWN 0x00000020
+#define BUTTON_LEFT 0x00000040
+#define BUTTON_RIGHT 0x00000080
+#define BUTTON_SELECT 0x00000100
+#define BUTTON_BACK 0x00000200
+#define BUTTON_MENU 0x00000400
+#define BUTTON_SCROLL_FWD 0x00000800
+#define BUTTON_SCROLL_BACK 0x00001000
+
+#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\
+ BUTTON_PLAY|BUTTON_TOUCHPAD)
+
+#define BUTTON_TOUCHPAD (BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|\
+ BUTTON_SELECT|BUTTON_BACK|BUTTON_MENU|\
+ BUTTON_SCROLL_FWD|BUTTON_SCROLL_BACK)
+
+#define POWEROFF_BUTTON BUTTON_POWER
+#define POWEROFF_COUNT 30
+
+extern void touchpad_set_sensitivity(int level);
+extern void touchpad_enable_device(bool en);
+
+#endif /* __BUTTON_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
new file mode 100644
index 0000000000..a389d2af42
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __I2C_TARGET_H__
+#define __I2C_TARGET_H__
+
+#define I2C_ASYNC_BUS_COUNT 3
+#define I2C_ASYNC_QUEUE_SIZE 4
+
+#define AK4376_BUS 0
+#define AK4376_ADDR 0x10
+
+#define FT6x06_BUS 1
+#define FT6x06_ADDR 0x38
+
+#define AXP173_BUS 2
+#define AXP173_ADDR 0x34
+
+#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
new file mode 100644
index 0000000000..c794da4000
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
@@ -0,0 +1,195 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "installer.h"
+#include "nand-x1000.h"
+#include "core_alloc.h"
+#include "file.h"
+
+#define INSTALL_SUCCESS 0
+#define ERR_FLASH_OPEN_FAILED (-1)
+#define ERR_FLASH_ENABLE_WP_FAILED (-2)
+#define ERR_FLASH_DISABLE_WP_FAILED (-3)
+#define ERR_FLASH_ERASE_FAILED (-4)
+#define ERR_FLASH_WRITE_FAILED (-5)
+#define ERR_FLASH_READ_FAILED (-6)
+#define ERR_OUT_OF_MEMORY (-7)
+#define ERR_CANNOT_READ_FILE (-8)
+#define ERR_CANNOT_WRITE_FILE (-9)
+#define ERR_WRONG_SIZE (-10)
+
+#define BOOT_IMAGE_SIZE (128 * 1024)
+
+static int install_from_buffer(const void* buf)
+{
+ if(nand_open())
+ return ERR_FLASH_OPEN_FAILED;
+
+ int status = INSTALL_SUCCESS;
+
+ if(nand_enable_writes(true)) {
+ status = ERR_FLASH_DISABLE_WP_FAILED;
+ goto _exit;
+ }
+
+ if(nand_erase_block(0)) {
+ status = ERR_FLASH_ERASE_FAILED;
+ goto _exit;
+ }
+
+ if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) {
+ status = ERR_FLASH_WRITE_FAILED;
+ goto _exit;
+ }
+
+ if(nand_enable_writes(false)) {
+ status = ERR_FLASH_ENABLE_WP_FAILED;
+ goto _exit;
+ }
+
+ _exit:
+ nand_close();
+ return status;
+}
+
+static int dump_to_buffer(void* buf)
+{
+ if(nand_open())
+ return ERR_FLASH_OPEN_FAILED;
+
+ int status = INSTALL_SUCCESS;
+
+ if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) {
+ status = ERR_FLASH_READ_FAILED;
+ goto _exit;
+ }
+
+ _exit:
+ nand_close();
+ return status;
+}
+
+int install_bootloader(const char* path)
+{
+ /* Allocate memory to hold image */
+ int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
+ if(handle < 0)
+ return ERR_OUT_OF_MEMORY;
+
+ int status = INSTALL_SUCCESS;
+ void* buffer = core_get_data(handle);
+
+ /* Open the boot image */
+ int fd = open(path, O_RDONLY);
+ if(fd < 0) {
+ status = ERR_CANNOT_READ_FILE;
+ goto _exit;
+ }
+
+ /* Check file size */
+ off_t fsize = filesize(fd);
+ if(fsize != BOOT_IMAGE_SIZE) {
+ status = ERR_WRONG_SIZE;
+ goto _exit;
+ }
+
+ /* Read the file into the buffer */
+ ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE);
+ if(cnt != BOOT_IMAGE_SIZE) {
+ status = ERR_CANNOT_READ_FILE;
+ goto _exit;
+ }
+
+ /* Perform the installation */
+ status = install_from_buffer(buffer);
+
+ _exit:
+ if(fd >= 0)
+ close(fd);
+ core_free(handle);
+ return status;
+}
+
+/* Dump the current bootloader to a file */
+int dump_bootloader(const char* path)
+{
+ /* Allocate memory to hold image */
+ int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
+ if(handle < 0)
+ return -1;
+
+ /* Read data from flash */
+ int fd = -1;
+ void* buffer = core_get_data(handle);
+ int status = dump_to_buffer(buffer);
+ if(status)
+ goto _exit;
+
+ /* Open file */
+ fd = open(path, O_CREAT|O_TRUNC|O_WRONLY);
+ if(fd < 0) {
+ status = ERR_CANNOT_WRITE_FILE;
+ goto _exit;
+ }
+
+ /* Write data to file */
+ ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE);
+ if(cnt != BOOT_IMAGE_SIZE) {
+ status = ERR_CANNOT_WRITE_FILE;
+ goto _exit;
+ }
+
+ _exit:
+ if(fd >= 0)
+ close(fd);
+ core_free(handle);
+ return status;
+}
+
+const char* installer_strerror(int rc)
+{
+ switch(rc) {
+ case INSTALL_SUCCESS:
+ return "Success";
+ case ERR_FLASH_OPEN_FAILED:
+ return "Can't open flash device";
+ case ERR_FLASH_ENABLE_WP_FAILED:
+ return "Couldn't re-enable write protect";
+ case ERR_FLASH_DISABLE_WP_FAILED:
+ return "Can't disable write protect";
+ case ERR_FLASH_ERASE_FAILED:
+ return "Flash erase failed";
+ case ERR_FLASH_WRITE_FAILED:
+ return "Flash write error";
+ case ERR_FLASH_READ_FAILED:
+ return "Flash read error";
+ case ERR_OUT_OF_MEMORY:
+ return "Out of memory";
+ case ERR_CANNOT_READ_FILE:
+ return "Error reading file";
+ case ERR_CANNOT_WRITE_FILE:
+ return "Error writing file";
+ case ERR_WRONG_SIZE:
+ return "Wrong file size";
+ default:
+ return "Unknown error";
+ }
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
new file mode 100644
index 0000000000..96f794d7df
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "lcd.h"
+#include "kernel.h"
+#include "lcd-x1000.h"
+#include "gpio-x1000.h"
+#include "system.h"
+
+#define CS_PIN (1 << 18)
+#define RD_PIN (1 << 16)
+
+static const uint32_t fiio_lcd_cmd_enable[] = {
+ /* Software reset */
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_UDELAY, 120000,
+ /* Sleep out */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 5000,
+ /* Memory access order */
+ LCD_INSTR_CMD, 0x36,
+ LCD_INSTR_DAT, 0x00,
+ /* Row and column address set */
+ LCD_INSTR_CMD, 0x2a,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, (LCD_WIDTH >> 8) & 0xff,
+ LCD_INSTR_DAT, (LCD_WIDTH & 0xff),
+ LCD_INSTR_CMD, 0x2b,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, (LCD_HEIGHT >> 8) & 0xff,
+ LCD_INSTR_DAT, (LCD_HEIGHT & 0xff),
+ /* Interface pixel format */
+ LCD_INSTR_CMD, 0x3a,
+ LCD_INSTR_DAT, 0x05,
+ /* Enable display inversion */
+ LCD_INSTR_CMD, 0x21,
+ /* Porch setting */
+ LCD_INSTR_CMD, 0xb2,
+ LCD_INSTR_DAT, 0x0c,
+ LCD_INSTR_DAT, 0x0c,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x33,
+ LCD_INSTR_DAT, 0x33,
+ /* Gate control */
+ LCD_INSTR_CMD, 0xb7,
+ LCD_INSTR_DAT, 0x35,
+ /* VCOM setting */
+ LCD_INSTR_CMD, 0xbb,
+ LCD_INSTR_DAT, 0x1f,
+ /* Backlight control 5 */
+ LCD_INSTR_CMD, 0xbc,
+ LCD_INSTR_DAT, 0xec,
+ /* Backlight control 6 */
+ LCD_INSTR_CMD, 0xbd,
+ LCD_INSTR_DAT, 0xfe,
+ /* Voltage settings */
+ LCD_INSTR_CMD, 0xc2,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_CMD, 0xc3,
+ LCD_INSTR_DAT, 0x19,
+ LCD_INSTR_CMD, 0xc4,
+ LCD_INSTR_DAT, 0x20,
+ /* Frame rate control */
+ LCD_INSTR_CMD, 0xc6,
+ LCD_INSTR_DAT, 0x0f, /* = 60 fps */
+ /* Power control 1 */
+ LCD_INSTR_CMD, 0xd0,
+ LCD_INSTR_DAT, 0xa4,
+ LCD_INSTR_DAT, 0xa1,
+ /* d6 Unknown */
+ LCD_INSTR_CMD, 0xd6,
+ LCD_INSTR_DAT, 0xa1,
+ /* Positive gamma correction */
+ LCD_INSTR_CMD, 0xe0,
+ LCD_INSTR_DAT, 0xd0,
+ LCD_INSTR_DAT, 0x06,
+ LCD_INSTR_DAT, 0x0c,
+ LCD_INSTR_DAT, 0x0a,
+ LCD_INSTR_DAT, 0x09,
+ LCD_INSTR_DAT, 0x0a,
+ LCD_INSTR_DAT, 0x32,
+ LCD_INSTR_DAT, 0x33,
+ LCD_INSTR_DAT, 0x49,
+ LCD_INSTR_DAT, 0x19,
+ LCD_INSTR_DAT, 0x14,
+ LCD_INSTR_DAT, 0x15,
+ LCD_INSTR_DAT, 0x2b,
+ LCD_INSTR_DAT, 0x34,
+ /* Negative gamma correction */
+ LCD_INSTR_CMD, 0xe1,
+ LCD_INSTR_DAT, 0xd0,
+ LCD_INSTR_DAT, 0x06,
+ LCD_INSTR_DAT, 0x0c,
+ LCD_INSTR_DAT, 0x0a,
+ LCD_INSTR_DAT, 0x09,
+ LCD_INSTR_DAT, 0x11,
+ LCD_INSTR_DAT, 0x37,
+ LCD_INSTR_DAT, 0x33,
+ LCD_INSTR_DAT, 0x49,
+ LCD_INSTR_DAT, 0x19,
+ LCD_INSTR_DAT, 0x14,
+ LCD_INSTR_DAT, 0x15,
+ LCD_INSTR_DAT, 0x2d,
+ LCD_INSTR_DAT, 0x34,
+ /* Tearing effect line ON, mode=0 (vsync signal) */
+ LCD_INSTR_CMD, 0x35,
+ LCD_INSTR_DAT, 0x00,
+ /* Display ON */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_END,
+};
+
+static const uint32_t fiio_lcd_cmd_sleep[] = {
+ /* Display OFF */
+ LCD_INSTR_CMD, 0x28,
+ /* Sleep IN */
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_UDELAY, 5000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t fiio_lcd_cmd_wake[] = {
+ /* Sleep OUT */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 5000,
+ /* Display ON */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_END,
+};
+
+static const uint8_t __attribute__((aligned(64)))
+ fiio_lcd_dma_wr_cmd[] = {0x00, 0x00, 0x00, 0x2c};
+
+const struct lcd_tgt_config lcd_tgt_config = {
+ .bus_width = 16,
+ .cmd_width = 8,
+ .use_6800_mode = 0,
+ .use_serial = 0,
+ .clk_polarity = 0,
+ .dc_polarity = 0,
+ .wr_polarity = 1,
+ .te_enable = 1,
+ .te_polarity = 1,
+ .te_narrow = 0,
+ .dma_wr_cmd_buf = &fiio_lcd_dma_wr_cmd,
+ .dma_wr_cmd_size = sizeof(fiio_lcd_dma_wr_cmd),
+};
+
+void lcd_tgt_enable(bool enable)
+{
+ if(enable) {
+ gpio_config(GPIO_A, 0xffff, GPIO_DEVICE(1));
+ gpio_config(GPIO_B, 0x1f << 16, GPIO_DEVICE(1));
+ gpio_config(GPIO_B, CS_PIN|RD_PIN, GPIO_OUTPUT(1));
+ mdelay(5);
+ gpio_out_level(GPIO_B, CS_PIN, 0);
+ lcd_set_clock(X1000_CLK_SCLK_A, 30000000);
+ lcd_exec_commands(&fiio_lcd_cmd_enable[0]);
+ } else {
+ lcd_exec_commands(&fiio_lcd_cmd_sleep[0]);
+ mdelay(115); /* ensure we wait a total of 120ms before power off */
+ gpio_config(GPIO_B, CS_PIN|RD_PIN, 0);
+ }
+}
+
+void lcd_tgt_sleep(bool sleep)
+{
+ if(sleep)
+ lcd_exec_commands(&fiio_lcd_cmd_sleep[0]);
+ else
+ lcd_exec_commands(&fiio_lcd_cmd_wake[0]);
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
new file mode 100644
index 0000000000..7c8a306bae
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "nand-x1000.h"
+#include "nand-target.h"
+#include "sfc-x1000.h"
+
+/* Unbelievably FiiO has completely disabled the use of ECC for this chip
+ * in their Linux kernel, even though it has perfectly good spare areas.
+ * There's no internal ECC either.
+ *
+ * Using nanddump to read the spare areas reveals they're filled with 0xff,
+ * and the publicly released Linux source has the ecc_strength set to 0.
+ */
+static const nand_chip_data ato25d1ga = {
+ .name = "ATO25D1GA",
+ .mf_id = 0x9b,
+ .dev_id = 0x12,
+ .dev_conf = NAND_INIT_SFC_DEV_CONF,
+ /* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz.
+ * Didn't find any issues doing this so might as well keep the behavior.
+ */
+ .clock_freq = NAND_INIT_CLOCK_SPEED,
+ .block_size = 64,
+ .page_size = 2048,
+ .spare_size = 64,
+ .rowaddr_width = 3,
+ .coladdr_width = 2,
+ .flags = NANDCHIP_FLAG_QUAD,
+};
+
+const nand_chip_desc target_nand_chip_descs[] = {
+ {&ato25d1ga, &nand_chip_ops_std},
+ {NULL, NULL},
+};
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h
new file mode 100644
index 0000000000..26a8b840c9
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __NAND_TARGET_H__
+#define __NAND_TARGET_H__
+
+/* The max page size (main + spare) of all NAND chips used by this target */
+#define NAND_MAX_PAGE_SIZE (2048 + 64)
+
+/* The clock source to use for the SFC controller. Note the SPL has special
+ * handling which ignores this choice, so it only applies to bootloader & app.
+ */
+#define NAND_CLOCK_SOURCE X1000_CLK_SCLK_A
+
+/* The clock speed to use for the SFC controller during chip identification */
+#define NAND_INIT_CLOCK_SPEED 150000000
+
+/* Initial value to program SFC_DEV_CONF register with */
+#define NAND_INIT_SFC_DEV_CONF \
+ jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \
+ CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1))
+
+#endif /* __NAND_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
new file mode 100644
index 0000000000..3eb3146d97
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "power.h"
+#include "adc.h"
+#include "system.h"
+#include "kernel.h"
+#include "axp173.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3470
+};
+
+/* the OF shuts down at this voltage */
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 }
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short const percent_to_volt_charge[11] =
+{
+ 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
+};
+
+#define AXP173_IRQ_PORT GPIO_B
+#define AXP173_IRQ_PIN (1 << 10)
+
+void power_init(void)
+{
+ /* Initialize driver */
+ i2c_x1000_set_freq(2, I2C_FREQ_400K);
+ axp173_init();
+
+ /* Set lowest sample rate */
+ axp173_adc_set_rate(AXP173_ADC_RATE_25HZ);
+
+ /* Ensure battery voltage ADC is enabled */
+ int bits = axp173_adc_get_enabled();
+ bits |= (1 << ADC_BATTERY_VOLTAGE);
+ axp173_adc_set_enabled(bits);
+
+ /* Turn on all power outputs */
+ i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x12, 0, 0x5f, NULL);
+ i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x80, 0, 0xc0, NULL);
+
+ /* Short delay to give power outputs time to stabilize */
+ mdelay(5);
+}
+
+void adc_init(void)
+{
+}
+
+void power_off(void)
+{
+ /* Set the shutdown bit */
+ i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0x32, 7, 1, NULL);
+ while(1);
+}
+
+bool charging_state(void)
+{
+ return axp173_battery_status() == AXP173_BATT_CHARGING;
+}
+
+int _battery_voltage(void)
+{
+ return axp173_adc_read(ADC_BATTERY_VOLTAGE);
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.c b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
new file mode 100644
index 0000000000..a47865e397
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "gpio-x1000.h"
+#include "kernel.h"
+
+#ifndef BOOTLOADER_SPL
+struct mutex gpio_z_mutex;
+#endif
+
+void gpio_init(void)
+{
+#ifndef BOOTLOADER_SPL
+ mutex_init(&gpio_z_mutex);
+#endif
+
+ /* Set all pins to input state */
+ for(int i = 0; i < 4; ++i) {
+ jz_clr(GPIO_INT(GPIO_Z), 0xffffffff);
+ jz_set(GPIO_MSK(GPIO_Z), 0xffffffff);
+ jz_set(GPIO_PAT1(GPIO_Z), 0xffffffff);
+ jz_clr(GPIO_PAT0(GPIO_Z), 0xffffffff);
+ REG_GPIO_Z_GID2LD = i;
+ }
+
+ /* Clear flag and disable pull resistor */
+ for(int i = 0; i < 4; ++i) {
+ jz_clr(GPIO_FLAG(i), 0xffffffff);
+ jz_set(GPIO_PULL(i), 0xffffffff);
+ }
+}
+
+void gpio_lock(void)
+{
+#ifndef BOOTLOADER_SPL
+ mutex_lock(&gpio_z_mutex);
+#endif
+}
+
+void gpio_unlock(void)
+{
+#ifndef BOOTLOADER_SPL
+ mutex_unlock(&gpio_z_mutex);
+#endif
+}
+
+void gpio_config(int port, unsigned pinmask, int func)
+{
+ unsigned intr = REG_GPIO_INT(port);
+ unsigned mask = REG_GPIO_MSK(port);
+ unsigned pat1 = REG_GPIO_PAT1(port);
+ unsigned pat0 = REG_GPIO_PAT0(port);
+
+ gpio_lock();
+ if(func & 8) jz_set(GPIO_INT(GPIO_Z), (intr & pinmask) ^ pinmask);
+ else jz_clr(GPIO_INT(GPIO_Z), (~intr & pinmask) ^ pinmask);
+ if(func & 4) jz_set(GPIO_MSK(GPIO_Z), (mask & pinmask) ^ pinmask);
+ else jz_clr(GPIO_MSK(GPIO_Z), (~mask & pinmask) ^ pinmask);
+ if(func & 2) jz_set(GPIO_PAT1(GPIO_Z), (pat1 & pinmask) ^ pinmask);
+ else jz_clr(GPIO_PAT1(GPIO_Z), (~pat1 & pinmask) ^ pinmask);
+ if(func & 1) jz_set(GPIO_PAT0(GPIO_Z), (pat0 & pinmask) ^ pinmask);
+ else jz_clr(GPIO_PAT0(GPIO_Z), (~pat0 & pinmask) ^ pinmask);
+ REG_GPIO_Z_GID2LD = port;
+ gpio_unlock();
+ gpio_set_pull(port, pinmask, func & 16);
+}
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.h b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
new file mode 100644
index 0000000000..f1a65b37b5
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __GPIO_X1000_H__
+#define __GPIO_X1000_H__
+
+/* GPIO API
+ * --------
+ *
+ * To assign a new function to a GPIO, call gpio_config(). This uses the
+ * hardware's GPIO Z facility to atomically most GPIO registers at once,
+ * so it can be used to make any state transition safely. Since GPIO Z is
+ * protected by a mutex, you can't call gpio_config() from interrupt context.
+ *
+ * If you need to use GPIO Z directly, then use gpio_lock() and gpio_unlock()
+ * to acquire the mutex.
+ *
+ * Depending on the current GPIO state, certain state transitions are safe to
+ * perform without locking, as they only change one register:
+ *
+ * - for pins in GPIO_OUTPUT state:
+ * - use gpio_out_level() to change the output level
+ *
+ * - for pins in GPIO_IRQ_LEVEL or GPIO_IRQ_EDGE state:
+ * - use gpio_irq_level() to change the trigger level
+ * - use gpio_irq_mask() to mask/unmask the IRQ
+ *
+ * - for pins in GPIO_DEVICE or GPIO_INPUT state:
+ * - no special transitions allowed
+ *
+ * - in all states:
+ * - use gpio_set_pull() to change the pull-up/pull-down state
+ */
+
+#include "x1000/gpio.h"
+
+/* GPIO port numbers */
+#define GPIO_A 0
+#define GPIO_B 1
+#define GPIO_C 2
+#define GPIO_D 3
+#define GPIO_Z 7
+
+/* GPIO function bits */
+#define GPIO_F_PULL 16
+#define GPIO_F_INT 8
+#define GPIO_F_MASK 4
+#define GPIO_F_PAT1 2
+#define GPIO_F_PAT0 1
+
+/* GPIO function numbers */
+#define GPIO_DEVICE(i) ((i)&3)
+#define GPIO_OUTPUT(i) (0x4|((i)&1))
+#define GPIO_INPUT 0x16
+#define GPIO_IRQ_LEVEL(i) (0x1c|((i)&1))
+#define GPIO_IRQ_EDGE(i) (0x1e|((i)&1))
+
+extern void gpio_init(void);
+extern void gpio_lock(void);
+extern void gpio_unlock(void);
+extern void gpio_config(int port, unsigned pinmask, int func);
+
+static inline void gpio_out_level(int port, unsigned pinmask, int level)
+{
+ if(level)
+ jz_set(GPIO_PAT0(port), pinmask);
+ else
+ jz_clr(GPIO_PAT0(port), pinmask);
+}
+
+#define gpio_irq_level gpio_out_level
+
+static inline void gpio_irq_mask(int port, unsigned pinmask, int masked)
+{
+ if(masked)
+ jz_set(GPIO_MSK(port), pinmask);
+ else
+ jz_clr(GPIO_MSK(port), pinmask);
+}
+
+#define gpio_enable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 0)
+#define gpio_disable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 1)
+
+static inline void gpio_set_pull(int port, unsigned pinmask, int state)
+{
+ if(state)
+ jz_set(GPIO_PULL(port), pinmask);
+ else
+ jz_clr(GPIO_PULL(port), pinmask);
+}
+
+#endif /* __GPIO_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/i2c-x1000.c b/firmware/target/mips/ingenic_x1000/i2c-x1000.c
new file mode 100644
index 0000000000..8bf606227b
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/i2c-x1000.c
@@ -0,0 +1,478 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* #define LOGF_ENABLE */
+#include "i2c-x1000.h"
+#include "system.h"
+#include "kernel.h"
+#include "panic.h"
+#include "logf.h"
+#include "gpio-x1000.h"
+#include "clk-x1000.h"
+#include "irq-x1000.h"
+#include "x1000/i2c.h"
+#include "x1000/cpm.h"
+
+#if I2C_ASYNC_BUS_COUNT != 3
+# error "Wrong I2C_ASYNC_BUS_COUNT (should be 3)"
+#endif
+
+#define FIFO_SIZE 64 /* Size of data FIFOs */
+#define FIFO_TX_THRESH 16 /* Wake up when TX FIFO gets to this level */
+#define FIFO_RX_SLACK 2 /* Slack space to leave, avoids RX FIFO overflow */
+
+typedef struct i2c_x1000_bus {
+ /* Hardware channel, this is just equal to i2c-async bus number. */
+ int chn;
+
+ /* Buffer/count usage depends on what phase the bus is processing:
+ *
+ * - Phase1: writing out descriptor's buffer[0] for both READs and WRITEs.
+ * - Phase2: writing out descriptor's buffer[1] for WRITEs, or issuing a
+ * series of read requests for READs.
+ *
+ * In phase1, buffer[1] and count[1] are equal to the descriptor's copy.
+ * buffer[0] and count[0] get incremented/decremented as we send bytes.
+ * Phase1 is only visited if we actually need to send bytes; if there
+ * would be no data in this phase then __i2c_async_submit() sets up the
+ * driver to go directly to phase2.
+ *
+ * Phase2 begins after phase1 writes out its last byte, or if phase1 was
+ * skipped at submit time. For WRITEs phase2 is identical to phase1 so we
+ * copy over buffer[1] and count[1] to buffer[0] and count[0], and zero
+ * out buffer[1] and count[1].
+ *
+ * For READs phase2 sets buffer[0] to NULL and count[0] equal to count[1].
+ * Now count[0] counts the number of bytes left to request, and count[1]
+ * counts the number of bytes left to receive. i2c_x1000_fifo_write() sees
+ * that buffer[0] is NULL and sends read requests instead of data bytes.
+ * buffer[1] is advanced by i2c_x1000_fifo_read() we receive bytes.
+ */
+ unsigned char* buffer[2];
+ int count[2];
+ bool phase1;
+
+ /* Copied fields from descriptor */
+ uint8_t bus_cond;
+ uint8_t tran_mode;
+
+ /* Counter to keep track of when to send end conditions */
+ int byte_cnt;
+ int byte_cnt_end;
+
+ /* Current bus frequency, used to calculate timeout durations */
+ long freq;
+
+ /* Timeout to reset the bus in case of buggy devices */
+ struct timeout tmo;
+
+ /* Flag used to indicate a reset is processing */
+ int resetting;
+} i2c_x1000_bus;
+
+static i2c_x1000_bus i2c_x1000_busses[3];
+
+static void i2c_x1000_fifo_write(i2c_x1000_bus* bus)
+{
+ int tx_free, tx_n;
+
+ /* Get free space in FIFO */
+ tx_free = FIFO_SIZE - REG_I2C_TXFLR(bus->chn);
+
+ _again:
+ /* Leave some slack space when reading. If we submit a full FIFO's worth
+ * of read requests, there's a small chance that a byte "on the wire" is
+ * unaccounted for and causes an RX FIFO overrun. Slack space is meant to
+ * avoid this situation.
+ */
+ if(bus->tran_mode == I2C_READ) {
+ tx_free -= FIFO_RX_SLACK;
+ if(tx_free <= 0)
+ goto _end;
+ }
+
+ /* Calculate number of bytes needed to send/request */
+ tx_n = MIN(tx_free, bus->count[0]);
+
+ /* Account for bytes we're about to send/request */
+ bus->count[0] -= tx_n;
+ tx_free -= tx_n;
+
+ for(; tx_n > 0; --tx_n) {
+ bus->byte_cnt += 1;
+
+ /* Read data byte or set read request bit */
+ uint32_t dc = bus->buffer[0] ? *bus->buffer[0]++ : jz_orm(I2C_DC, CMD);
+
+ /* Check for first byte & apply RESTART.
+ * Note the HW handles RESTART automatically when changing the
+ * direction of the transfer, so we don't need to check for that.
+ */
+ if(bus->byte_cnt == 1 && (bus->bus_cond & I2C_RESTART))
+ dc |= jz_orm(I2C_DC, RESTART);
+
+ /* Check for last byte & apply STOP */
+ if(bus->byte_cnt == bus->byte_cnt_end && (bus->bus_cond & I2C_STOP))
+ dc |= jz_orm(I2C_DC, STOP);
+
+ /* Add entry to FIFO */
+ REG_I2C_DC(bus->chn) = dc;
+ }
+
+ /* FIFO full and current phase still has data to send.
+ * Configure interrupt to fire when there's a good amount of free space.
+ */
+ if(bus->count[0] > 0) {
+ _end:
+ REG_I2C_TXTL(bus->chn) = FIFO_TX_THRESH;
+ jz_writef(I2C_INTMSK(bus->chn), TXEMP(1), RXFL(0));
+ return;
+ }
+
+ /* Advance to second phase if needed */
+ if(bus->phase1 && bus->count[1] > 0) {
+ bus->buffer[0] = bus->tran_mode == I2C_WRITE ? bus->buffer[1] : NULL;
+ bus->count[0] = bus->count[1];
+ bus->phase1 = false;
+
+ /* Submit further data if possible; else wait for TX space */
+ if(tx_free > 0)
+ goto _again;
+ else
+ goto _end;
+ }
+
+ /* All phases are done. Now we just need to wake up when the whole
+ * operation is complete, either by waiting for TX to drain or RX to
+ * fill to the appropriate level. */
+ if(bus->tran_mode == I2C_WRITE) {
+ REG_I2C_TXTL(bus->chn) = 0;
+ jz_writef(I2C_INTMSK(bus->chn), TXEMP(1), RXFL(0));
+ } else {
+ REG_I2C_RXTL(bus->chn) = bus->count[1] - 1;
+ jz_writef(I2C_INTMSK(bus->chn), TXEMP(0), RXFL(1));
+ }
+}
+
+static void i2c_x1000_fifo_read(i2c_x1000_bus* bus)
+{
+ /* Get number of bytes in the RX FIFO */
+ int rx_n = REG_I2C_RXFLR(bus->chn);
+
+ /* Shouldn't happen, but check just in case */
+ if(rx_n > bus->count[1]) {
+ panicf("i2c_x1000(%d): expected %d bytes in RX fifo, got %d",
+ bus->chn, bus->count[1], rx_n);
+ }
+
+ /* Fill buffer with data from FIFO */
+ bus->count[1] -= rx_n;
+ for(; rx_n != 0; --rx_n) {
+ *bus->buffer[1]++ = jz_readf(I2C_DC(bus->chn), DAT);
+ }
+}
+
+static void i2c_x1000_interrupt(i2c_x1000_bus* bus)
+{
+ int intr = REG_I2C_INTST(bus->chn);
+ int status = I2C_STATUS_OK;
+
+ /* Bus error; we can't prevent this from happening. As I understand
+ * it, we cannot get a TXABT when the bus is idle, so it should be
+ * safe to leave this interrupt unmasked all the time.
+ */
+ if(intr & jz_orm(I2C_INTST, TXABT)) {
+ logf("i2c_x1000(%d): got TXABT (%08lx)",
+ bus->chn, REG_I2C_ABTSRC(bus->chn));
+
+ REG_I2C_CTXABT(bus->chn);
+ status = I2C_STATUS_ERROR;
+ goto _done;
+ }
+
+ /* FIFO errors shouldn't occur unless driver did something dumb */
+ if(intr & jz_orm(I2C_INTST, RXUF, TXOF, RXOF)) {
+#if 1
+ panicf("i2c_x1000(%d): fifo error (%08x)", bus->chn, intr);
+#else
+ /* This is how the error condition would be cleared */
+ REG_I2C_CTXOF(bus->chn);
+ REG_I2C_CRXOF(bus->chn);
+ REG_I2C_CRXUF(bus->chn);
+ status = I2C_STATUS_ERROR;
+ goto _done;
+#endif
+ }
+
+ /* Read from FIFO on reads, and check if we have sent/received
+ * the expected amount of data. If so, complete the descriptor. */
+ if(bus->tran_mode == I2C_READ) {
+ i2c_x1000_fifo_read(bus);
+ if(bus->count[1] == 0)
+ goto _done;
+ } else if(bus->count[0] == 0) {
+ goto _done;
+ }
+
+ /* Still need to send or request data -- issue commands to FIFO */
+ i2c_x1000_fifo_write(bus);
+ return;
+
+ _done:
+ jz_writef(I2C_INTMSK(bus->chn), TXEMP(0), RXFL(0));
+ timeout_cancel(&bus->tmo);
+ __i2c_async_complete_callback(bus->chn, status);
+}
+
+void I2C0(void)
+{
+ i2c_x1000_interrupt(&i2c_x1000_busses[0]);
+}
+
+void I2C1(void)
+{
+ i2c_x1000_interrupt(&i2c_x1000_busses[1]);
+}
+
+void I2C2(void)
+{
+ i2c_x1000_interrupt(&i2c_x1000_busses[2]);
+}
+
+static int i2c_x1000_bus_timeout(struct timeout* tmo)
+{
+ /* Buggy device is preventing the operation from completing, so we
+ * can't do much except reset the bus and hope for the best. Device
+ * drivers can aid us by detecting the TIMEOUT status we return and
+ * resetting the device to get it out of a bugged state. */
+
+ i2c_x1000_bus* bus = (i2c_x1000_bus*)tmo->data;
+ switch(bus->resetting) {
+ default:
+ /* Start of reset. Disable the controller */
+ REG_I2C_INTMSK(bus->chn) = 0;
+ bus->resetting = 1;
+ jz_writef(I2C_ENABLE(bus->chn), ACTIVE(0));
+ return 1;
+ case 1:
+ /* Check if controller is disabled yet */
+ if(jz_readf(I2C_ENBST(bus->chn), ACTIVE))
+ return 1;
+
+ /* Wait 10 ms after disabling to give time for bus to clear */
+ bus->resetting = 2;
+ return HZ/100;
+ case 2:
+ /* Re-enable the controller */
+ bus->resetting = 3;
+ jz_writef(I2C_ENABLE(bus->chn), ACTIVE(1));
+ return 1;
+ case 3:
+ /* Check that controller is enabled */
+ if(jz_readf(I2C_ENBST(bus->chn), ACTIVE) == 0)
+ return 1;
+
+ /* Reset complete */
+ bus->resetting = 0;
+ jz_overwritef(I2C_INTMSK(bus->chn), RXFL(0), TXEMP(0),
+ TXABT(1), TXOF(1), RXOF(1), RXUF(1));
+ __i2c_async_complete_callback(bus->chn, I2C_STATUS_TIMEOUT);
+ return 0;
+ }
+}
+
+void __i2c_async_submit(int busnr, i2c_descriptor* desc)
+{
+ i2c_x1000_bus* bus = &i2c_x1000_busses[busnr];
+
+ if(desc->tran_mode == I2C_READ) {
+ if(desc->count[0] > 0) {
+ /* Handle initial write as phase1 */
+ bus->buffer[0] = desc->buffer[0];
+ bus->count[0] = desc->count[0];
+ bus->phase1 = true;
+ } else {
+ /* No initial write, skip directly to phase2 */
+ bus->buffer[0] = NULL;
+ bus->count[0] = desc->count[1];
+ bus->phase1 = false;
+ }
+
+ /* Set buffer/count for phase2 read */
+ bus->buffer[1] = desc->buffer[1];
+ bus->count[1] = desc->count[1];
+ } else {
+ /* Writes always have to have buffer[0] populated; buffer[1] is
+ * allowed to be NULL (and thus count[1] = 0). This matches our
+ * phase logic so no need for anything special
+ */
+ bus->buffer[0] = desc->buffer[0];
+ bus->count[0] = desc->count[0];
+ bus->buffer[1] = desc->buffer[1];
+ bus->count[1] = desc->count[1];
+ bus->phase1 = true;
+ }
+
+ /* Save bus condition and transfer mode */
+ bus->bus_cond = desc->bus_cond;
+ bus->tran_mode = desc->tran_mode;
+
+ /* Byte counter is used to check for first and last byte and apply
+ * the requested end mode */
+ bus->byte_cnt = 0;
+ bus->byte_cnt_end = desc->count[0] + desc->count[1];
+
+ /* Ensure interrupts are cleared */
+ REG_I2C_CINT(busnr);
+
+ /* Program target address */
+ jz_overwritef(I2C_TAR(busnr), ADDR(desc->slave_addr & ~I2C_10BIT_ADDR),
+ 10BITS(desc->slave_addr & I2C_10BIT_ADDR ? 1 : 0));
+
+ /* Do the initial FIFO fill; this sets up the needed interrupts. */
+ i2c_x1000_fifo_write(bus);
+
+ /* Software timeout to deal with buggy slave devices that pull the bus
+ * high forever and leave us hanging. Use 100ms + whatever time should
+ * be needed to handle data transmission. Account for 9 bits per byte
+ * because of the ACKs necessary after each byte.
+ */
+ long ticks = (HZ/10) + (HZ * 9 * bus->byte_cnt_end / bus->freq);
+ timeout_register(&bus->tmo, i2c_x1000_bus_timeout, ticks, (intptr_t)bus);
+}
+
+void i2c_init(void)
+{
+ /* Initialize core */
+ __i2c_async_init();
+
+ /* Initialize our bus data structures */
+ for(int i = 0; i < 3; ++i) {
+ i2c_x1000_busses[i].chn = i;
+ i2c_x1000_busses[i].freq = 0;
+ i2c_x1000_busses[i].resetting = 0;
+ }
+}
+
+/* Stuff only required during initialization is below, basically the same as
+ * the old driver except for how the IRQs are initially set up. */
+
+static const struct {
+ int port;
+ unsigned pins;
+ int func;
+} i2c_x1000_gpio_data[] = {
+ {GPIO_B, 3 << 23, GPIO_DEVICE(0)},
+ {GPIO_C, 3 << 26, GPIO_DEVICE(0)},
+ /* Note: I2C1 is also on the following pins (normally used by LCD) */
+ /* {GPIO_A, 3 << 0, GPIO_DEVICE(2)}, */
+ {GPIO_D, 3 << 0, GPIO_DEVICE(1)},
+};
+
+static void i2c_x1000_gate(int chn, int gate)
+{
+ switch(chn) {
+ case 0: jz_writef(CPM_CLKGR, I2C0(gate)); break;
+ case 1: jz_writef(CPM_CLKGR, I2C1(gate)); break;
+ case 2: jz_writef(CPM_CLKGR, I2C2(gate)); break;
+ default: break;
+ }
+}
+
+static void i2c_x1000_enable(int chn)
+{
+ /* Enable controller */
+ jz_writef(I2C_ENABLE(chn), ACTIVE(1));
+ while(jz_readf(I2C_ENBST(chn), ACTIVE) == 0);
+
+ /* Set up interrutpts */
+ jz_overwritef(I2C_INTMSK(chn), RXFL(0), TXEMP(0),
+ TXABT(1), TXOF(1), RXOF(1), RXUF(1));
+ system_enable_irq(IRQ_I2C(chn));
+}
+
+static void i2c_x1000_disable(int chn)
+{
+ /* Disable interrupts */
+ system_disable_irq(IRQ_I2C(chn));
+ REG_I2C_INTMSK(chn) = 0;
+
+ /* Disable controller */
+ jz_writef(I2C_ENABLE(chn), ACTIVE(0));
+ while(jz_readf(I2C_ENBST(chn), ACTIVE));
+}
+
+void i2c_x1000_set_freq(int chn, int freq)
+{
+ /* Store frequency */
+ i2c_x1000_busses[chn].freq = freq;
+
+ /* Disable the channel if previously active */
+ i2c_x1000_gate(chn, 0);
+ if(jz_readf(I2C_ENBST(chn), ACTIVE) == 1)
+ i2c_x1000_disable(chn);
+
+ /* Request to shut down the channel */
+ if(freq == 0) {
+ i2c_x1000_gate(chn, 1);
+ return;
+ }
+
+ /* Calculate timing parameters */
+ unsigned pclk = clk_get(X1000_CLK_PCLK);
+ unsigned t_SU_DAT = pclk / (freq * 8);
+ unsigned t_HD_DAT = pclk / (freq * 12);
+ unsigned t_LOW = pclk / (freq * 2);
+ unsigned t_HIGH = pclk / (freq * 2);
+ if(t_SU_DAT > 1) t_SU_DAT -= 1;
+ if(t_SU_DAT > 255) t_SU_DAT = 255;
+ if(t_SU_DAT == 0) t_SU_DAT = 0;
+ if(t_HD_DAT > 0xffff) t_HD_DAT = 0xfff;
+ if(t_LOW < 8) t_LOW = 8;
+ if(t_HIGH < 6) t_HIGH = 6;
+
+ /* Control register setting */
+ unsigned reg = jz_orf(I2C_CON, SLVDIS(1), RESTART(1), MD(1));
+ if(freq <= I2C_FREQ_100K)
+ reg |= jz_orf(I2C_CON, SPEED_V(100K));
+ else
+ reg |= jz_orf(I2C_CON, SPEED_V(400K));
+
+ /* Write the new controller settings */
+ jz_write(I2C_CON(chn), reg);
+ jz_write(I2C_SDASU(chn), t_SU_DAT);
+ jz_write(I2C_SDAHD(chn), t_HD_DAT);
+ if(freq <= I2C_FREQ_100K) {
+ jz_write(I2C_SLCNT(chn), t_LOW);
+ jz_write(I2C_SHCNT(chn), t_HIGH);
+ } else {
+ jz_write(I2C_FLCNT(chn), t_LOW);
+ jz_write(I2C_FHCNT(chn), t_HIGH);
+ }
+
+ /* Claim pins */
+ gpio_config(i2c_x1000_gpio_data[chn].port,
+ i2c_x1000_gpio_data[chn].pins,
+ i2c_x1000_gpio_data[chn].func);
+
+ /* Enable the controller */
+ i2c_x1000_enable(chn);
+}
diff --git a/firmware/target/mips/ingenic_x1000/i2c-x1000.h b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
new file mode 100644
index 0000000000..e76624d511
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __I2C_X1000_H__
+#define __I2C_X1000_H__
+
+#include "i2c-async.h"
+
+#define I2C_FREQ_100K 100000
+#define I2C_FREQ_400K 400000
+
+extern void i2c_init(void);
+
+/* Configure the I2C controller prior to use.
+ *
+ * - freq: frequency of SCL, should be <= 400 KHz and >= 25 KHz
+ * - use I2C_FREQ_100K for 100 KHz
+ * - use I2C_FREQ_400K for 400 Khz
+ * - use 0 to disable the controller completely
+ * - frequencies below 25 KHz will violate timing constraints
+ *
+ * TODO: move this to the i2c-async API, it's simple enough
+ */
+extern void i2c_x1000_set_freq(int chn, int freq);
+
+#endif /* __I2C_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/irq-x1000.h b/firmware/target/mips/ingenic_x1000/irq-x1000.h
new file mode 100644
index 0000000000..849e436dcf
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/irq-x1000.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __IRQ_X1000_H__
+#define __IRQ_X1000_H__
+
+/* INTC(0) interrupts */
+#define IRQ0_DMIC 0
+#define IRQ0_AIC 1
+#define IRQ0_SFC 7
+#define IRQ0_SSI 8
+#define IRQ0_PDMA 10
+#define IRQ0_PDMAD 11
+#define IRQ0_GPIO3 14
+#define IRQ0_GPIO2 15
+#define IRQ0_GPIO1 16
+#define IRQ0_GPIO0 17
+#define IRQ0_OTG 21
+#define IRQ0_AES 23
+#define IRQ0_TCU2 25
+#define IRQ0_TCU1 26
+#define IRQ0_TCU0 27
+#define IRQ0_CIM 30
+#define IRQ0_LCD 31
+
+/* INTC(1) interrupts */
+#define IRQ1_RTC 0
+#define IRQ1_MSC1 4
+#define IRQ1_MSC0 5
+#define IRQ1_SCC 6
+#define IRQ1_PCM 8
+#define IRQ1_HARB2 12
+#define IRQ1_HARB0 14
+#define IRQ1_CPM 15
+#define IRQ1_UART2 17
+#define IRQ1_UART1 18
+#define IRQ1_UART0 19
+#define IRQ1_DDR 20
+#define IRQ1_EFUSE 22
+#define IRQ1_MAC 23
+#define IRQ1_I2C2 26
+#define IRQ1_I2C1 27
+#define IRQ1_I2C0 28
+#define IRQ1_I2C(c) (28 - (c))
+#define IRQ1_PDMAM 29
+#define IRQ1_JPEG 30
+
+/* Unified IRQ numbers */
+#define IRQ_DMIC IRQ0_DMIC
+#define IRQ_AIC IRQ0_AIC
+#define IRQ_SFC IRQ0_SFC
+#define IRQ_SSI IRQ0_SSI
+#define IRQ_PDMA IRQ0_PDMA
+#define IRQ_PDMAD IRQ0_PDMAD
+#define IRQ_GPIO3 IRQ0_GPIO3
+#define IRQ_GPIO2 IRQ0_GPIO2
+#define IRQ_GPIO1 IRQ0_GPIO1
+#define IRQ_GPIO0 IRQ0_GPIO0
+#define IRQ_OTG IRQ0_OTG
+#define IRQ_AES IRQ0_AES
+#define IRQ_TCU2 IRQ0_TCU2
+#define IRQ_TCU1 IRQ0_TCU1
+#define IRQ_TCU0 IRQ0_TCU0
+#define IRQ_CIM IRQ0_CIM
+#define IRQ_LCD IRQ0_LCD
+#define IRQ_RTC (32+IRQ1_RTC)
+#define IRQ_MSC1 (32+IRQ1_MSC1)
+#define IRQ_MSC0 (32+IRQ1_MSC0)
+#define IRQ_SCC (32+IRQ1_SCC)
+#define IRQ_PCM (32+IRQ1_PCM)
+#define IRQ_HARB2 (32+IRQ1_HARB2)
+#define IRQ_HARB0 (32+IRQ1_HARB0)
+#define IRQ_CPM (32+IRQ1_CPM)
+#define IRQ_UART2 (32+IRQ1_UART2)
+#define IRQ_UART1 (32+IRQ1_UART1)
+#define IRQ_UART0 (32+IRQ1_UART0)
+#define IRQ_DDR (32+IRQ1_DDR)
+#define IRQ_EFUSE (32+IRQ1_EFUSE)
+#define IRQ_MAC (32+IRQ1_MAC)
+#define IRQ_I2C2 (32+IRQ1_I2C2)
+#define IRQ_I2C1 (32+IRQ1_I2C1)
+#define IRQ_I2C0 (32+IRQ1_I2C0)
+#define IRQ_I2C(c) (32+IRQ1_I2C(c))
+#define IRQ_PDMAM (32+IRQ1_PDMAM)
+#define IRQ_JPEG (32+IRQ1_JPEG)
+#define IRQ_GPIO(port, pin) (64 + 32*(port) + (pin))
+
+#define IRQ_IS_GROUP0(irq) (((irq) & 0xffffff20) == 0x00)
+#define IRQ_IS_GROUP1(irq) (((irq) & 0xffffff20) == 0x20)
+#define IRQ_IS_GPIO(irq) ((irq) >= 64)
+
+#define IRQ_TO_GROUP0(irq) (irq)
+#define IRQ_TO_GROUP1(irq) ((irq) - 32)
+#define IRQ_TO_GPIO_PORT(irq) (((irq) - 64) >> 5)
+#define IRQ_TO_GPIO_PIN(irq) (((irq) - 64) & 0x1f)
+
+#endif /* __IRQ_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/kernel-x1000.c b/firmware/target/mips/ingenic_x1000/kernel-x1000.c
new file mode 100644
index 0000000000..c8105a3446
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/kernel-x1000.c
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "kernel.h"
+#include "system.h"
+#include "x1000/ost.h"
+
+/* TODO: implement a CPU frequency switching policy based on CPU utilization
+ *
+ * The basic assumption is that the workload consumes a fixed number of CPU
+ * cycles per second on average (= utilization), so we can set the frequency
+ * based on that value. Audio playback should fit this usage pattern well, so
+ * it's a good fit for Rockbox.
+ *
+ * It's easier to understand in terms of fluid flow -- we need to keep
+ * a reservoir of water topped up, CPU frequency is the inflow rate, and
+ * CPU utilization is the outflow rate. The goal is to avoid running dry
+ * and minimize the inflow rate.
+ *
+ * The only tricky part here is handing usage spikes -- CPU frequency has to
+ * increase faster than utilization or there's a risk of audio dropouts.
+ *
+ * Rockbox CPU boost could be used as a hint to scale up frequency faster.
+ * If that's not necessary to get good results, HAVE_ADJUSTABLE_CPU_FREQ can
+ * be disabled entirely.
+ */
+
+#define CPU_IDLE_SAMPLES 100
+
+void tick_start(unsigned interval_in_ms)
+{
+ jz_writef(OST_CTRL, PRESCALE1_V(BY_16));
+ jz_write(OST_1DFR, interval_in_ms*(X1000_EXCLK_FREQ/16000));
+ jz_write(OST_1CNT, 0);
+ jz_write(OST_1FLG, 0);
+ jz_write(OST_1MSK, 0);
+ jz_setf(OST_ENABLE, OST1);
+}
+
+void OST(void)
+{
+ /* CPU idle time accounting */
+ uint32_t now = __ost_read32();
+ uint32_t div = now - __cpu_idle_reftick;
+ if(div != 0) {
+ uint32_t fraction = 1000 * __cpu_idle_ticks / div;
+ __cpu_idle_avg += fraction - __cpu_idle_avg / CPU_IDLE_SAMPLES;
+ __cpu_idle_cur = __cpu_idle_avg / CPU_IDLE_SAMPLES;
+ __cpu_idle_ticks = 0;
+ __cpu_idle_reftick = now;
+ }
+
+ /* Call regular kernel tick */
+ jz_write(OST_1FLG, 0);
+ call_tick_tasks();
+}
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
new file mode 100644
index 0000000000..aadf93c8ff
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
@@ -0,0 +1,477 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "lcd.h"
+#include "system.h"
+#include "kernel.h"
+#include "lcd-x1000.h"
+#include "dma-x1000.h"
+#include "irq-x1000.h"
+#include "x1000/lcd.h"
+#include "x1000/cpm.h"
+#include <stdint.h>
+#include <string.h>
+
+#define LCD_DMA_CMD_SOFINT (1 << 31)
+#define LCD_DMA_CMD_EOFINT (1 << 30)
+#define LCD_DMA_CMD_COMMAND (1 << 29)
+#define LCD_DMA_CMD_FRM_EN (1 << 26)
+
+#define LCD_DMA_CNT_BPP_15BIT ((4 << 27)|(1<<30))
+#define LCD_DMA_CNT_BPP_16BIT (4 << 27)
+#define LCD_DMA_CNT_BPP_18BIT_OR_24BIT (5 << 27)
+
+struct lcd_dma_desc {
+ uint32_t da; /* Next descriptor address */
+ uint32_t sa; /* Source buffer address */
+ uint32_t fid; /* Frame ID */
+ uint32_t cmd; /* Command bits */
+ uint32_t osz; /* OFFSIZE register */
+ uint32_t pw; /* page width */
+ uint32_t cnt; /* CNUM / CPOS, depending on LCD_DMA_CMD_COMMAND bit */
+ uint32_t fsz; /* Frame size (set to 0 for commands) */
+} __attribute__((aligned(32)));
+
+/* We need two descriptors, one for framebuffer write command and one for
+ * frame data. Even if no command is needed we need a dummy command descriptor
+ * with cnt=0, or the hardware will refuse to transfer the frame data.
+ *
+ * First descriptor always has to be a command (lcd_dma_desc[0] here) or
+ * the hardware will give up.
+ */
+static struct lcd_dma_desc lcd_dma_desc[2];
+
+/* Shadow copy of main framebuffer, needed to avoid tearing */
+static fb_data shadowfb[LCD_HEIGHT*LCD_WIDTH] __attribute__((aligned(64)));
+
+/* Signals DMA copy to shadow FB is done */
+static volatile int fbcopy_done;
+
+/* True if we're in sleep mode */
+static bool lcd_sleeping = false;
+
+/* Check if running with interrupts disabled (eg: panic screen) */
+#define lcd_panic_mode \
+ UNLIKELY((read_c0_status() & 1) == 0)
+
+static void lcd_init_controller(const struct lcd_tgt_config* cfg)
+{
+ /* Set MCFG/MCFG_NEW according to target interface settings */
+ unsigned mcfg = 0, mcfg_new = 0;
+
+ switch(cfg->cmd_width) {
+ case 8: mcfg |= BF_LCD_MCFG_CWIDTH_V(8BIT); break;
+ case 9: mcfg |= BF_LCD_MCFG_CWIDTH_V(16BIT_OR_9BIT); break;
+ case 16: mcfg |= BF_LCD_MCFG_CWIDTH_V(16BIT_OR_9BIT); break;
+ case 18: mcfg |= BF_LCD_MCFG_CWIDTH_V(18BIT); break;
+ case 24: mcfg |= BF_LCD_MCFG_CWIDTH_V(24BIT); break;
+ default: break;
+ }
+
+ if(cfg->cmd_width == 9)
+ mcfg_new |= BM_LCD_MCFG_NEW_CMD_9BIT;
+
+ switch(cfg->bus_width) {
+ case 8: mcfg_new |= BF_LCD_MCFG_NEW_DWIDTH_V(8BIT); break;
+ case 9: mcfg_new |= BF_LCD_MCFG_NEW_DWIDTH_V(9BIT); break;
+ case 16: mcfg_new |= BF_LCD_MCFG_NEW_DWIDTH_V(16BIT); break;
+ case 18: mcfg_new |= BF_LCD_MCFG_NEW_DWIDTH_V(18BIT); break;
+ case 24: mcfg_new |= BF_LCD_MCFG_NEW_DWIDTH_V(24BIT); break;
+ default: break;
+ }
+
+ if(lcd_tgt_config.use_serial)
+ mcfg_new |= jz_orf(LCD_MCFG_NEW, DTYPE_V(SERIAL), CTYPE_V(SERIAL));
+ else
+ mcfg_new |= jz_orf(LCD_MCFG_NEW, DTYPE_V(PARALLEL), CTYPE_V(PARALLEL));
+
+ jz_vwritef(mcfg_new, LCD_MCFG_NEW,
+ 6800_MODE(lcd_tgt_config.use_6800_mode),
+ CSPLY(lcd_tgt_config.wr_polarity ? 0 : 1),
+ RSPLY(lcd_tgt_config.dc_polarity),
+ CLKPLY(lcd_tgt_config.clk_polarity));
+
+ /* Program the configuration. Note we cannot enable TE signal at
+ * this stage, because the panel will need to be configured first.
+ */
+ jz_write(LCD_MCFG, mcfg);
+ jz_write(LCD_MCFG_NEW, mcfg_new);
+ jz_writef(LCD_MCTRL, NARROW_TE(0), TE_INV(0), NOT_USE_TE(1),
+ DCSI_SEL(0), MIPI_SLCD(0), FAST_MODE(1), GATE_MASK(0),
+ DMA_MODE(1), DMA_START(0), DMA_TX_EN(0));
+ jz_writef(LCD_WTIME, DHTIME(0), DLTIME(0), CHTIME(0), CLTIME(0));
+ jz_writef(LCD_TASH, TAH(0), TAS(0));
+ jz_write(LCD_SMWT, 0);
+
+ /* DMA settings */
+ jz_writef(LCD_CTRL, BURST_V(64WORD),
+ EOFM(1), SOFM(0), IFUM(0), QDM(0),
+ BEDN(0), PEDN(0), ENABLE(0));
+ jz_write(LCD_DAH, LCD_WIDTH);
+ jz_write(LCD_DAV, LCD_HEIGHT);
+}
+
+static void lcd_fbcopy_dma_cb(int evt);
+
+static void lcd_init_descriptors(const struct lcd_tgt_config* cfg)
+{
+ struct lcd_dma_desc* desc = &lcd_dma_desc[0];
+ int cmdsize = cfg->dma_wr_cmd_size / 4;
+
+ /* Set up the command descriptor */
+ desc[0].da = PHYSADDR(&desc[1]);
+ desc[0].sa = PHYSADDR(cfg->dma_wr_cmd_buf);
+ desc[0].fid = 0xc0;
+ desc[0].cmd = LCD_DMA_CMD_COMMAND | cmdsize;
+ desc[0].osz = 0;
+ desc[0].pw = 0;
+ desc[0].fsz = 0;
+ switch(cfg->cmd_width) {
+ case 8: desc[0].cnt = 4*cmdsize; break;
+ case 9:
+ case 16: desc[0].cnt = 2*cmdsize; break;
+ case 18:
+ case 24: desc[0].cnt = cmdsize; break;
+ default: break;
+ }
+
+ /* Set up the frame descriptor */
+ desc[1].da = PHYSADDR(&desc[0]);
+ desc[1].sa = PHYSADDR(shadowfb);
+ desc[1].fid = 0xf0;
+ desc[1].cmd = LCD_DMA_CMD_EOFINT | LCD_DMA_CMD_FRM_EN |
+ (LCD_WIDTH * LCD_HEIGHT * sizeof(fb_data) / 4);
+ desc[1].osz = 0;
+ desc[1].pw = 0;
+ desc[1].fsz = (LCD_WIDTH - 1) | ((LCD_HEIGHT - 1) << 12);
+#if LCD_DEPTH == 16
+ desc[1].cnt = LCD_DMA_CNT_BPP_16BIT;
+#elif LCD_DEPTH == 24
+ desc[1].cnt = LCD_DMA_CNT_BPP_18BIT_OR_24BIT;
+#else
+# error "unsupported LCD bit depth"
+#endif
+
+ /* Commit LCD DMA descriptors */
+ commit_dcache_range(&desc[0], 2*sizeof(struct lcd_dma_desc));
+
+ /* Set fbcopy channel callback */
+ dma_set_callback(DMA_CHANNEL_FBCOPY, lcd_fbcopy_dma_cb);
+}
+
+static void lcd_fbcopy_dma_cb(int evt)
+{
+ (void)evt;
+ fbcopy_done = 1;
+}
+
+static void lcd_fbcopy_dma_run(dma_desc* d)
+{
+ if(lcd_panic_mode) {
+ /* Can't use DMA if interrupts are off, so just do a memcpy().
+ * Doesn't need to be efficient, since AFAIK the panic screen is
+ * the only place that can update the LCD with interrupts disabled. */
+ memcpy(shadowfb, FBADDR(0, 0), LCD_WIDTH*LCD_HEIGHT*sizeof(fb_data));
+ commit_dcache();
+ return;
+ }
+
+ commit_dcache_range(d, sizeof(struct dma_desc));
+
+ /* Start the transfer */
+ fbcopy_done = 0;
+ REG_DMA_CHN_DA(DMA_CHANNEL_FBCOPY) = PHYSADDR(d);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_FBCOPY), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_FBCOPY);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_FBCOPY), CTE(1));
+
+ while(!fbcopy_done);
+}
+
+static void lcd_fbcopy_dma_full(void)
+{
+ dma_desc d;
+ d.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(1), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ d.sa = PHYSADDR(FBADDR(0, 0));
+ d.ta = PHYSADDR(shadowfb);
+ d.tc = LCD_WIDTH * LCD_HEIGHT * sizeof(fb_data);
+ d.sd = 0;
+ d.rt = jz_orf(DMA_CHN_RT, TYPE_V(AUTO));
+ d.pad0 = 0;
+ d.pad1 = 0;
+ lcd_fbcopy_dma_run(&d);
+}
+
+/* NOTE: DMA stride mode can only transfer up to 255 blocks at once.
+ *
+ * - for LCD_STRIDEFORMAT == VERTICAL_STRIDE, keep width <= 255
+ * - for LCD_STRIDEFORMAT == HORIZONTAL_STRIDE, keep height <= 255
+ */
+static void lcd_fbcopy_dma_partial1(int x, int y, int width, int height)
+{
+ int stride = STRIDE_MAIN(LCD_WIDTH - width, LCD_HEIGHT - height);
+
+ dma_desc d;
+ d.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(1), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(stride ? 1 : 0), TIE(1), LINK(0));
+ d.sa = PHYSADDR(FBADDR(x, y));
+ d.ta = PHYSADDR(&shadowfb[STRIDE_MAIN(y * LCD_WIDTH + x,
+ x * LCD_HEIGHT + y)]);
+ d.rt = jz_orf(DMA_CHN_RT, TYPE_V(AUTO));
+ d.pad0 = 0;
+ d.pad1 = 0;
+
+ if(stride) {
+ stride *= sizeof(fb_data);
+ d.sd = (stride << 16) | stride;
+ d.tc = (STRIDE_MAIN(height, width) << 16) |
+ (STRIDE_MAIN(width, height) * sizeof(fb_data));
+ } else {
+ d.sd = 0;
+ d.tc = width * height * sizeof(fb_data);
+ }
+
+ lcd_fbcopy_dma_run(&d);
+}
+
+#if STRIDE_MAIN(LCD_HEIGHT, LCD_WIDTH) > 255
+static void lcd_fbcopy_dma_partial(int x, int y, int width, int height)
+{
+ do {
+ int count = MIN(STRIDE_MAIN(height, width), 255);
+
+ lcd_fbcopy_dma_partial1(x, y, STRIDE_MAIN(width, count),
+ STRIDE_MAIN(count, height));
+
+ STRIDE_MAIN(height, width) -= count;
+ STRIDE_MAIN(y, x) += count;
+ } while(STRIDE_MAIN(height, width) != 0);
+}
+#else
+# define lcd_fbcopy_dma_partial lcd_fbcopy_dma_partial1
+#endif
+
+static void lcd_dma_start(void)
+{
+ /* Set format conversion bit, seems necessary for DMA mode */
+ jz_writef(LCD_MCFG_NEW, FMT_CONV(1));
+
+ /* Program vsync configuration */
+ jz_writef(LCD_MCTRL, NARROW_TE(lcd_tgt_config.te_narrow),
+ TE_INV(lcd_tgt_config.te_polarity ? 0 : 1),
+ NOT_USE_TE(lcd_tgt_config.te_enable ? 0 : 1));
+
+ /* Begin DMA transfer. Need to start a dummy frame or else we will
+ * not be able to pass lcd_wait_frame() at the first lcd_update(). */
+ jz_write(LCD_STATE, 0);
+ jz_write(LCD_DA, PHYSADDR(&lcd_dma_desc[0]));
+ jz_writef(LCD_MCTRL, DMA_MODE(1), DMA_START(1), DMA_TX_EN(1));
+ jz_writef(LCD_CTRL, ENABLE(1));
+}
+
+static void lcd_dma_stop(void)
+{
+ /* Stop the DMA transfer */
+ jz_writef(LCD_CTRL, ENABLE(0));
+ jz_writef(LCD_MCTRL, DMA_TX_EN(0));
+
+ /* Wait for disable to take effect */
+ while(jz_readf(LCD_STATE, QD) == 0);
+ jz_writef(LCD_STATE, QD(0));
+
+ /* Clear format conversion bit, disable vsync */
+ jz_writef(LCD_MCFG_NEW, FMT_CONV(0));
+ jz_writef(LCD_MCTRL, NARROW_TE(0), TE_INV(0), NOT_USE_TE(1));
+}
+
+static bool lcd_wait_frame(void)
+{
+ /* Bail out if DMA is not enabled */
+ int irq = disable_irq_save();
+ int bit = jz_readf(LCD_CTRL, ENABLE);
+ restore_irq(irq);
+ if(!bit)
+ return false;
+
+ /* Usual case -- wait for EOF, wait for FIFO to drain, clear EOF */
+ while(jz_readf(LCD_STATE, EOF) == 0);
+ while(jz_readf(LCD_MSTATE, BUSY));
+ jz_writef(LCD_STATE, EOF(0));
+ return true;
+}
+
+static void lcd_send(uint32_t d)
+{
+ while(jz_readf(LCD_MSTATE, BUSY));
+ REG_LCD_MDATA = d;
+}
+
+void lcd_set_clock(x1000_clk_t clk, uint32_t freq)
+{
+ uint32_t in_freq = clk_get(clk);
+ uint32_t div = clk_calc_div(in_freq, freq);
+
+ jz_writef(CPM_LPCDR, CE(1), CLKDIV(div - 1),
+ CLKSRC(clk == X1000_CLK_MPLL ? 1 : 0));
+ while(jz_readf(CPM_LPCDR, BUSY));
+ jz_writef(CPM_LPCDR, CE(0));
+}
+
+void lcd_exec_commands(const uint32_t* cmdseq)
+{
+ while(*cmdseq != LCD_INSTR_END) {
+ uint32_t instr = *cmdseq++;
+ uint32_t d = 0;
+ switch(instr) {
+ case LCD_INSTR_CMD:
+ d = jz_orf(LCD_MDATA, TYPE_V(CMD));
+ /* fallthrough */
+
+ case LCD_INSTR_DAT:
+ d |= *cmdseq++;
+ lcd_send(d);
+ break;
+
+ case LCD_INSTR_UDELAY:
+ udelay(*cmdseq++);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void lcd_init_device(void)
+{
+ jz_writef(CPM_CLKGR, LCD(0));
+
+ lcd_init_controller(&lcd_tgt_config);
+ lcd_init_descriptors(&lcd_tgt_config);
+
+ lcd_tgt_enable(true);
+
+ lcd_dma_start();
+}
+
+#ifdef HAVE_LCD_SHUTDOWN
+void lcd_shutdown(void)
+{
+ if(lcd_sleeping)
+ lcd_tgt_sleep(false);
+ else if(jz_readf(LCD_CTRL, ENABLE))
+ lcd_dma_stop();
+
+ lcd_tgt_enable(false);
+ jz_writef(CPM_CLKGR, LCD(1));
+}
+#endif
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+bool lcd_active(void)
+{
+ return jz_readf(LCD_CTRL, ENABLE);
+}
+
+void lcd_enable(bool en)
+{
+ /* Must disable IRQs to turn off the running LCD */
+ int irq = disable_irq_save();
+ int bit = jz_readf(LCD_CTRL, ENABLE);
+ if(bit && !en)
+ lcd_dma_stop();
+ restore_irq(irq);
+
+ /* Deal with sleep mode */
+#ifdef LCD_X1000_FASTSLEEP
+ if(bit && !en) {
+ lcd_tgt_sleep(true);
+ lcd_sleeping = true;
+ } else
+#endif
+ if(!bit && en && lcd_sleeping) {
+ lcd_tgt_sleep(false);
+ lcd_sleeping = false;
+ }
+
+ /* Handle turning the LCD back on */
+ if(!bit && en)
+ lcd_dma_start();
+}
+#endif
+
+#if defined(HAVE_LCD_SLEEP)
+#if defined(LCD_X1000_FASTSLEEP)
+# error "Do not define HAVE_LCD_SLEEP if target has LCD_X1000_FASTSLEEP"
+#endif
+
+void lcd_sleep(void)
+{
+ if(!lcd_sleeping) {
+ lcd_enable(false);
+ lcd_tgt_sleep(true);
+ lcd_sleeping = true;
+ }
+}
+#endif
+
+void lcd_update(void)
+{
+ if(!lcd_wait_frame())
+ return;
+
+ commit_dcache();
+ lcd_fbcopy_dma_full();
+ jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1));
+}
+
+void lcd_update_rect(int x, int y, int width, int height)
+{
+ /* Clamp the coordinates */
+ if(x < 0) {
+ width += x;
+ x = 0;
+ }
+
+ if(y < 0) {
+ height += y;
+ y = 0;
+ }
+
+ if(width > LCD_WIDTH - x)
+ width = LCD_WIDTH - x;
+
+ if(height > LCD_HEIGHT - y)
+ height = LCD_HEIGHT - y;
+
+ if(width < 0 || height < 0)
+ return;
+
+ if(!lcd_wait_frame())
+ return;
+
+ commit_dcache();
+ lcd_fbcopy_dma_partial(x, y, width, height);
+ jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1));
+}
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.h b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
new file mode 100644
index 0000000000..96085ac207
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __LCD_X1000_H__
+#define __LCD_X1000_H__
+
+/* NOTICE: if adding LCD support for a new X1000 target, please take a look
+ * at the implementation in case there's any difficulties; there may be some
+ * parts that need adjusting. The X1000 LCD interface is poorly documented
+ * and it might be necessary to change some settings by trial and error to
+ * match the panel. */
+
+#include "clk-x1000.h"
+#include <stdbool.h>
+
+#define LCD_INSTR_CMD 0
+#define LCD_INSTR_DAT 1
+#define LCD_INSTR_UDELAY 2
+#define LCD_INSTR_END 3
+
+struct lcd_tgt_config {
+ /* Data bus width, in bits */
+ int bus_width: 8;
+
+ /* Command bus width, in bits */
+ int cmd_width: 8;
+
+ /* 1 = use 6800 timings, 0 = use 8080 timings */
+ int use_6800_mode: 1;
+
+ /* 1 = serial interface, 0 = parallel interface */
+ int use_serial: 1;
+
+ /* Clock active edge: 0 = falling edge, 1 = rising edge */
+ int clk_polarity: 1;
+
+ /* DC pin levels: 1 = data high, command low; 0 = data low, command high */
+ int dc_polarity: 1;
+
+ /* WR pin level during idle: 1 = keep high; 0 = keep low */
+ int wr_polarity: 1;
+
+ /* 1 to enable vsync, so DMA transfer is synchronized with TE signal */
+ int te_enable: 1;
+
+ /* Active level of TE signal: 1 = high, 0 = low */
+ int te_polarity: 1;
+
+ /* 1 = support narrow TE signal (<=3 pixel clocks); 0 = don't support */
+ int te_narrow: 1;
+
+ /* Commands used to initiate a framebuffer write. Buffer must be
+ * aligned to 64-byte boundary and size must be a multiple of 4,
+ * regardless of the command bus width. */
+ const void* dma_wr_cmd_buf;
+ size_t dma_wr_cmd_size;
+};
+
+/* Static configuration for the target's LCD, must be defined by target. */
+extern const struct lcd_tgt_config lcd_tgt_config;
+
+/* Set the pixel clock. Valid clock sources are SCLK_A and MPLL. */
+extern void lcd_set_clock(x1000_clk_t clksrc, uint32_t freq);
+
+/* Execute a sequence of LCD commands. Should only be called from
+ * lcd_tgt_ctl_enable() and lcd_tgt_ctl_sleep().
+ *
+ * The array should be a list of pairs (instr, arg), with LCD_INSTR_END
+ * as the last entry.
+ *
+ * - LCD_INSTR_CMD, cmd: issue command write of 'cmd'
+ * - LCD_INSTR_DAT, dat: issue data write of 'dat'
+ * - LCD_INSTR_UDELAY, us: call udelay(us)
+ */
+extern void lcd_exec_commands(const uint32_t* cmdseq);
+
+/* Enable/disable the LCD controller.
+ *
+ * - On enabling: power on the LCD, set the pixel clock with lcd_set_clock(),
+ * and use lcd_exec_commands() to send any needed initialization commands.
+ *
+ * - On disabling: use lcd_exec_commands() to send shutdown commands to the
+ * controller and disable the LCD power supply.
+ */
+extern void lcd_tgt_enable(bool on);
+
+/* Enter or exit sleep mode to save power, normally by sending the necessary
+ * commands with lcd_exec_commands().
+ */
+extern void lcd_tgt_sleep(bool sleep);
+
+#endif /* __LCD_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c
new file mode 100644
index 0000000000..62aa76988c
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c
@@ -0,0 +1,904 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "panic.h"
+#include "msc-x1000.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "clk-x1000.h"
+#include "x1000/msc.h"
+#include "x1000/cpm.h"
+#include <string.h>
+#include <stddef.h>
+
+/* #define LOGF_ENABLE */
+#include "logf.h"
+
+/* TODO - this needs some auditing to better handle errors
+ *
+ * There should be a clearer code path involving errors. Especially we should
+ * ensure that removing the card always resets the driver to a sane state.
+ */
+
+static const msc_config msc_configs[] = {
+#ifdef FIIO_M3K
+#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
+#define msc0_cd_interrupt GPIOB06
+ {
+ .msc_nr = 0,
+ .msc_type = MSC_TYPE_SD,
+ .bus_width = 4,
+ .label = "microSD",
+ .cd_gpio = {GPIO_B, 1 << 6, 0},
+ },
+#else
+# error "Please add X1000 MSC config"
+#endif
+ {.msc_nr = -1},
+};
+
+static const msc_config* msc_lookup_config(int msc)
+{
+ for(int i = 0; i < MSC_COUNT; ++i)
+ if(msc_configs[i].msc_nr == msc)
+ return &msc_configs[i];
+ return NULL;
+}
+
+static msc_drv msc_drivers[MSC_COUNT];
+
+/* ---------------------------------------------------------------------------
+ * Initialization
+ */
+
+static void msc_gate_clock(int msc, bool gate)
+{
+ int bit;
+ if(msc == 0)
+ bit = BM_CPM_CLKGR_MSC0;
+ else
+ bit = BM_CPM_CLKGR_MSC1;
+
+ if(gate)
+ REG_CPM_CLKGR |= bit;
+ else
+ REG_CPM_CLKGR &= ~bit;
+}
+
+static void msc_init_one(msc_drv* d, int msc)
+{
+ /* Lookup config */
+ d->drive_nr = -1;
+ d->config = msc_lookup_config(msc);
+ if(!d->config) {
+ d->msc_nr = -1;
+ return;
+ }
+
+ /* Initialize driver state */
+ d->msc_nr = msc;
+ d->driver_flags = 0;
+ d->clk_status = 0;
+ d->cmdat_def = jz_orf(MSC_CMDAT, RTRG_V(GE32), TTRG_V(LE32));
+ d->req = NULL;
+ d->iflag_done = 0;
+ d->card_present = 1;
+ d->req_running = 0;
+ mutex_init(&d->lock);
+ semaphore_init(&d->cmd_done, 1, 0);
+
+ /* Ensure correct clock source */
+ jz_writef(CPM_MSC0CDR, CE(1), CLKDIV(0),
+ CLKSRC(MSC_CLOCK_SOURCE == X1000_CLK_MPLL ? 1 : 0));
+ while(jz_readf(CPM_MSC0CDR, BUSY));
+ jz_writef(CPM_MSC0CDR, CE(0));
+
+ /* Initialize the hardware */
+ msc_gate_clock(msc, false);
+ msc_full_reset(d);
+ system_enable_irq(msc == 0 ? IRQ_MSC0 : IRQ_MSC1);
+
+ /* Configure bus pins */
+ int port, device;
+ unsigned pins;
+ if(msc == 0) {
+ port = GPIO_A;
+ device = 1;
+ switch(d->config->bus_width) {
+ case 8: pins = 0x3ff << 16; break;
+ case 4: pins = 0x03f << 20; break;
+ case 1: pins = 0x007 << 23; break;
+ default: pins = 0; break;
+ }
+ } else {
+ port = GPIO_C;
+ device = 0;
+ switch(d->config->bus_width) {
+ case 4: pins = 0x3f; break;
+ case 1: pins = 0x07; break;
+ default: pins = 0; break;
+ }
+ }
+
+ gpio_config(port, pins, GPIO_DEVICE(device));
+
+ /* Setup the card detect IRQ */
+ if(d->config->cd_gpio.pin) {
+ port = d->config->cd_gpio.port;
+ pins = d->config->cd_gpio.pin;
+ int level = (REG_GPIO_PIN(port) & pins) ? 1 : 0;
+ if(level != d->config->cd_gpio.active_level)
+ d->card_present = 0;
+
+ gpio_config(port, pins, GPIO_IRQ_EDGE(level ? 0 : 1));
+ gpio_enable_irq(port, pins);
+ }
+}
+
+void msc_init(void)
+{
+ /* Only do this once -- each storage subsystem calls us in its init */
+ static bool done = false;
+ if(done)
+ return;
+ done = true;
+
+ /* Set up each MSC driver according to msc_configs */
+ for(int i = 0; i < MSC_COUNT; ++i)
+ msc_init_one(&msc_drivers[i], i);
+}
+
+msc_drv* msc_get(int type, int index)
+{
+ for(int i = 0, m = 0; i < MSC_COUNT; ++i) {
+ if(msc_drivers[i].config == NULL)
+ continue;
+ if(type == MSC_TYPE_ANY || msc_drivers[i].config->msc_type == type)
+ if(index == m++)
+ return &msc_drivers[i];
+ }
+
+ return NULL;
+}
+
+msc_drv* msc_get_by_drive(int drive_nr)
+{
+ for(int i = 0; i < MSC_COUNT; ++i)
+ if(msc_drivers[i].drive_nr == drive_nr)
+ return &msc_drivers[i];
+ return NULL;
+}
+
+void msc_lock(msc_drv* d)
+{
+ mutex_lock(&d->lock);
+}
+
+void msc_unlock(msc_drv* d)
+{
+ mutex_unlock(&d->lock);
+}
+
+void msc_full_reset(msc_drv* d)
+{
+ msc_lock(d);
+ msc_set_clock_mode(d, MSC_CLK_AUTOMATIC);
+ msc_set_speed(d, MSC_SPEED_INIT);
+ msc_set_width(d, 1);
+ msc_ctl_reset(d);
+ d->driver_flags = 0;
+ memset(&d->cardinfo, 0, sizeof(tCardInfo));
+ msc_unlock(d);
+}
+
+bool msc_card_detect(msc_drv* d)
+{
+ if(!d->config->cd_gpio.pin)
+ return true;
+
+ int l = REG_GPIO_PIN(d->config->cd_gpio.port) & d->config->cd_gpio.pin;
+ l = l ? 1 : 0;
+ return l == d->config->cd_gpio.active_level;
+}
+
+/* ---------------------------------------------------------------------------
+ * Controller API
+ */
+
+void msc_ctl_reset(msc_drv* d)
+{
+ /* Ingenic code suggests a reset changes clkrt */
+ int clkrt = REG_MSC_CLKRT(d->msc_nr);
+
+ /* Send reset -- bit is NOT self clearing */
+ jz_overwritef(MSC_CTRL(d->msc_nr), RESET(1));
+ udelay(100);
+ jz_writef(MSC_CTRL(d->msc_nr), RESET(0));
+
+ /* Verify reset in the status register */
+ long deadline = current_tick + HZ;
+ while(jz_readf(MSC_STAT(d->msc_nr), IS_RESETTING) &&
+ current_tick < deadline) {
+ sleep(1);
+ }
+
+ /* Ensure the clock state is as expected */
+ if(d->clk_status & MSC_CLKST_AUTO)
+ jz_writef(MSC_LPM(d->msc_nr), ENABLE(1));
+ else if(d->clk_status & MSC_CLKST_ENABLE)
+ jz_overwritef(MSC_CTRL(d->msc_nr), CLOCK_V(START));
+ else
+ jz_overwritef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP));
+
+ /* Clear and mask interrupts */
+ REG_MSC_IMASK(d->msc_nr) = 0xffffffff;
+ REG_MSC_IFLAG(d->msc_nr) = 0xffffffff;
+
+ /* Restore clkrt */
+ REG_MSC_CLKRT(d->msc_nr) = clkrt;
+}
+
+void msc_set_clock_mode(msc_drv* d, int mode)
+{
+ int cur_mode = (d->clk_status & MSC_CLKST_AUTO) ? MSC_CLK_AUTOMATIC
+ : MSC_CLK_MANUAL;
+ if(mode == cur_mode)
+ return;
+
+ d->clk_status &= ~MSC_CLKST_ENABLE;
+ if(mode == MSC_CLK_AUTOMATIC) {
+ d->clk_status |= MSC_CLKST_AUTO;
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP));
+ jz_writef(MSC_LPM(d->msc_nr), ENABLE(1));
+ } else {
+ d->clk_status &= ~MSC_CLKST_AUTO;
+ jz_writef(MSC_LPM(d->msc_nr), ENABLE(0));
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP));
+ }
+}
+
+void msc_enable_clock(msc_drv* d, bool enable)
+{
+ if(d->clk_status & MSC_CLKST_AUTO)
+ return;
+
+ bool is_enabled = (d->clk_status & MSC_CLKST_ENABLE);
+ if(enable == is_enabled)
+ return;
+
+ if(enable) {
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(START));
+ d->clk_status |= MSC_CLKST_ENABLE;
+ } else {
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP));
+ d->clk_status &= ~MSC_CLKST_ENABLE;
+ }
+}
+
+void msc_set_speed(msc_drv* d, int rate)
+{
+ /* Shut down clock while we change frequencies */
+ if(d->clk_status & MSC_CLKST_ENABLE)
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP));
+
+ /* Wait for clock to go idle */
+ while(jz_readf(MSC_STAT(d->msc_nr), CLOCK_EN))
+ sleep(1);
+
+ /* freq1 is output by MSCxDIV; freq2 is output by MSC_CLKRT */
+ uint32_t freq1 = rate;
+ uint32_t freq2 = rate;
+ if(freq1 < MSC_SPEED_FAST)
+ freq1 = MSC_SPEED_FAST;
+
+ /* Handle MSCxDIV */
+ uint32_t src_freq = clk_get(MSC_CLOCK_SOURCE) / 2;
+ uint32_t div = clk_calc_div(src_freq, freq1);
+ if(d->msc_nr == 0) {
+ jz_writef(CPM_MSC0CDR, CE(1), CLKDIV(div - 1));
+ while(jz_readf(CPM_MSC0CDR, BUSY));
+ jz_writef(CPM_MSC0CDR, CE(0));
+ } else {
+ jz_writef(CPM_MSC1CDR, CE(1), CLKDIV(div - 1));
+ while(jz_readf(CPM_MSC1CDR, BUSY));
+ jz_writef(CPM_MSC1CDR, CE(0));
+ }
+
+ /* Handle MSC_CLKRT */
+ uint32_t clkrt = clk_calc_shift(src_freq/div, freq2);
+ REG_MSC_CLKRT(d->msc_nr) = clkrt;
+
+ /* Handle frequency dependent timing settings
+ * TODO - these settings might be SD specific...
+ */
+ uint32_t out_freq = (src_freq/div) >> clkrt;
+ if(out_freq > MSC_SPEED_FAST) {
+ jz_writef(MSC_LPM(d->msc_nr),
+ DRV_SEL_V(RISE_EDGE_DELAY_QTR_PHASE),
+ SMP_SEL_V(RISE_EDGE_DELAYED));
+ } else {
+ jz_writef(MSC_LPM(d->msc_nr),
+ DRV_SEL_V(FALL_EDGE),
+ SMP_SEL_V(RISE_EDGE));
+ }
+
+ /* Restart clock if it was running before */
+ if(d->clk_status & MSC_CLKST_ENABLE)
+ jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(START));
+}
+
+void msc_set_width(msc_drv* d, int width)
+{
+ /* Bus width is controlled per command with MSC_CMDAT. */
+ if(width == 8)
+ jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(8BIT));
+ else if(width == 4)
+ jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(4BIT));
+ else
+ jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(1BIT));
+}
+
+/* ---------------------------------------------------------------------------
+ * Request API
+ */
+
+/* Note -- this must only be called with IRQs disabled */
+static void msc_finish_request(msc_drv* d, int status)
+{
+ REG_MSC_IMASK(d->msc_nr) = 0xffffffff;
+ REG_MSC_IFLAG(d->msc_nr) = 0xffffffff;
+ if(d->req->flags & MSC_RF_DATA)
+ jz_writef(MSC_DMAC(d->msc_nr), ENABLE(0));
+
+ d->req->status = status;
+ d->req_running = 0;
+ d->iflag_done = 0;
+ timeout_cancel(&d->cmd_tmo);
+ semaphore_release(&d->cmd_done);
+}
+
+static int msc_req_timeout(struct timeout* tmo)
+{
+ msc_drv* d = (msc_drv*)tmo->data;
+ msc_async_abort(d, MSC_REQ_LOCKUP);
+ return 0;
+}
+
+void msc_async_start(msc_drv* d, msc_req* r)
+{
+ /* Determined needed cmdat and interrupts */
+ unsigned cmdat = d->cmdat_def;
+ d->iflag_done = jz_orm(MSC_IFLAG, END_CMD_RES);
+
+ cmdat |= jz_orf(MSC_CMDAT, RESP_FMT(r->resptype & ~MSC_RESP_BUSY));
+ if(r->resptype & MSC_RESP_BUSY)
+ cmdat |= jz_orm(MSC_CMDAT, BUSY);
+
+ if(r->flags & MSC_RF_INIT)
+ cmdat |= jz_orm(MSC_CMDAT, INIT);
+
+ if(r->flags & MSC_RF_DATA) {
+ cmdat |= jz_orm(MSC_CMDAT, DATA_EN);
+ if(r->flags & MSC_RF_PROG)
+ d->iflag_done = jz_orm(MSC_IFLAG, WR_ALL_DONE);
+ else
+ d->iflag_done = jz_orm(MSC_IFLAG, DMA_DATA_DONE);
+ }
+
+ if(r->flags & MSC_RF_WRITE)
+ cmdat |= jz_orm(MSC_CMDAT, WRITE_READ);
+
+ if(r->flags & MSC_RF_AUTO_CMD12)
+ cmdat |= jz_orm(MSC_CMDAT, AUTO_CMD12);
+
+ if(r->flags & MSC_RF_ABORT)
+ cmdat |= jz_orm(MSC_CMDAT, IO_ABORT);
+
+ unsigned imask = jz_orm(MSC_IMASK,
+ CRC_RES_ERROR, CRC_READ_ERROR, CRC_WRITE_ERROR,
+ TIME_OUT_RES, TIME_OUT_READ, END_CMD_RES);
+ imask |= d->iflag_done;
+
+ /* Program the controller */
+ if(r->flags & MSC_RF_DATA) {
+ REG_MSC_NOB(d->msc_nr) = r->nr_blocks;
+ REG_MSC_BLKLEN(d->msc_nr) = r->block_len;
+ }
+
+ REG_MSC_CMD(d->msc_nr) = r->command;
+ REG_MSC_ARG(d->msc_nr) = r->argument;
+ REG_MSC_CMDAT(d->msc_nr) = cmdat;
+
+ REG_MSC_IFLAG(d->msc_nr) = imask;
+ REG_MSC_IMASK(d->msc_nr) &= ~imask;
+
+ if(r->flags & MSC_RF_DATA) {
+ d->dma_desc.nda = 0;
+ d->dma_desc.mem = PHYSADDR(r->data);
+ d->dma_desc.len = r->nr_blocks * r->block_len;
+ d->dma_desc.cmd = 2; /* ID=0, ENDI=1, LINK=0 */
+ commit_dcache_range(&d->dma_desc, sizeof(d->dma_desc));
+
+ if(r->flags & MSC_RF_WRITE)
+ commit_dcache_range(r->data, d->dma_desc.len);
+ else
+ discard_dcache_range(r->data, d->dma_desc.len);
+
+ /* TODO - should use MODE_SEL bit? what value of INCR? */
+ unsigned long addr_off = ((unsigned long)r->data) & 3;
+ jz_writef(MSC_DMAC(d->msc_nr), MODE_SEL(0), INCR(0), DMASEL(0),
+ ALIGN_EN(addr_off != 0 ? 1 : 0), ADDR_OFFSET(addr_off));
+ REG_MSC_DMANDA(d->msc_nr) = PHYSADDR(&d->dma_desc);
+ }
+
+ /* Begin processing */
+ d->req = r;
+ d->req_running = 1;
+ jz_writef(MSC_CTRL(d->msc_nr), START_OP(1));
+ if(r->flags & MSC_RF_DATA)
+ jz_writef(MSC_DMAC(d->msc_nr), ENABLE(1));
+
+ /* TODO: calculate a suitable lower value for the lockup timeout.
+ *
+ * The SD spec defines timings based on the number of blocks transferred,
+ * see sec. 4.6.2 "Read, write, and erase timeout conditions". This should
+ * reduce the long delays which happen if errors occur.
+ *
+ * Also need to check if registers MSC_RDTO / MSC_RESTO are correctly set.
+ */
+ timeout_register(&d->cmd_tmo, msc_req_timeout, 10*HZ, (intptr_t)d);
+}
+
+void msc_async_abort(msc_drv* d, int status)
+{
+ int irq = disable_irq_save();
+ if(d->req_running) {
+ logf("msc%d: async abort status:%d", d->msc_nr, status);
+ msc_finish_request(d, status);
+ }
+
+ restore_irq(irq);
+}
+
+int msc_async_wait(msc_drv* d, int timeout)
+{
+ if(semaphore_wait(&d->cmd_done, timeout) == OBJ_WAIT_TIMEDOUT)
+ return MSC_REQ_INCOMPLETE;
+
+ return d->req->status;
+}
+
+int msc_request(msc_drv* d, msc_req* r)
+{
+ msc_async_start(d, r);
+ return msc_async_wait(d, TIMEOUT_BLOCK);
+}
+
+/* ---------------------------------------------------------------------------
+ * Command response handling
+ */
+
+static void msc_read_response(msc_drv* d)
+{
+ unsigned res = REG_MSC_RES(d->msc_nr);
+ unsigned dat;
+ switch(d->req->resptype) {
+ case MSC_RESP_R1:
+ case MSC_RESP_R1B:
+ case MSC_RESP_R3:
+ case MSC_RESP_R6:
+ case MSC_RESP_R7:
+ dat = res << 24;
+ res = REG_MSC_RES(d->msc_nr);
+ dat |= res << 8;
+ res = REG_MSC_RES(d->msc_nr);
+ dat |= res & 0xff;
+ d->req->response[0] = dat;
+ break;
+
+ case MSC_RESP_R2:
+ for(int i = 0; i < 4; ++i) {
+ dat = res << 24;
+ res = REG_MSC_RES(d->msc_nr);
+ dat |= res << 8;
+ res = REG_MSC_RES(d->msc_nr);
+ dat |= res >> 8;
+ d->req->response[i] = dat;
+ }
+
+ break;
+
+ default:
+ return;
+ }
+}
+
+static int msc_check_sd_response(msc_drv* d)
+{
+ if(d->req->resptype == MSC_RESP_R1 ||
+ d->req->resptype == MSC_RESP_R1B) {
+ if(d->req->response[0] & SD_R1_CARD_ERROR) {
+ logf("msc%d: R1 card error: %08x", d->msc_nr, d->req->response[0]);
+ return MSC_REQ_CARD_ERR;
+ }
+ }
+
+ return MSC_REQ_SUCCESS;
+}
+
+static int msc_check_response(msc_drv* d)
+{
+ switch(d->config->msc_type) {
+ case MSC_TYPE_SD:
+ return msc_check_sd_response(d);
+ default:
+ /* TODO - implement msc_check_response for MMC and CE-ATA */
+ return 0;
+ }
+}
+
+/* ---------------------------------------------------------------------------
+ * Interrupt handlers
+ */
+
+static void msc_interrupt(msc_drv* d)
+{
+ const unsigned tmo_bits = jz_orm(MSC_IFLAG, TIME_OUT_READ, TIME_OUT_RES);
+ const unsigned crc_bits = jz_orm(MSC_IFLAG, CRC_RES_ERROR,
+ CRC_READ_ERROR, CRC_WRITE_ERROR);
+ const unsigned err_bits = tmo_bits | crc_bits;
+
+ unsigned iflag = REG_MSC_IFLAG(d->msc_nr) & ~REG_MSC_IMASK(d->msc_nr);
+ bool handled = false;
+
+ /* In case card was removed */
+ if(!msc_card_detect(d)) {
+ msc_finish_request(d, MSC_REQ_EXTRACTED);
+ return;
+ }
+
+ /* Check for errors */
+ if(iflag & err_bits) {
+ int st;
+ if(iflag & crc_bits)
+ st = MSC_REQ_CRC_ERR;
+ else if(iflag & tmo_bits)
+ st = MSC_REQ_TIMEOUT;
+ else
+ st = MSC_REQ_ERROR;
+
+ msc_finish_request(d, st);
+ return;
+ }
+
+ /* Read and check the command response */
+ if(iflag & BM_MSC_IFLAG_END_CMD_RES) {
+ msc_read_response(d);
+ int st = msc_check_response(d);
+ if(st == MSC_REQ_SUCCESS) {
+ jz_writef(MSC_IMASK(d->msc_nr), END_CMD_RES(1));
+ jz_overwritef(MSC_IFLAG(d->msc_nr), END_CMD_RES(1));
+ handled = true;
+ } else {
+ msc_finish_request(d, st);
+ return;
+ }
+ }
+
+ /* Check if the "done" interrupt is signaled */
+ if(iflag & d->iflag_done) {
+ /* Discard after DMA in case of hardware cache prefetching.
+ * Only needed for read operations.
+ */
+ if((d->req->flags & MSC_RF_DATA) != 0 &&
+ (d->req->flags & MSC_RF_WRITE) == 0) {
+ discard_dcache_range(d->req->data,
+ d->req->block_len * d->req->nr_blocks);
+ }
+
+ msc_finish_request(d, MSC_REQ_SUCCESS);
+ return;
+ }
+
+ if(!handled) {
+ panicf("msc%d: irq bug! iflag:%08x raw_iflag:%08lx imask:%08lx",
+ d->msc_nr, iflag, REG_MSC_IFLAG(d->msc_nr), REG_MSC_IMASK(d->msc_nr));
+ }
+}
+
+static int msc_cd_callback(struct timeout* tmo)
+{
+ msc_drv* d = (msc_drv*)tmo->data;
+
+ /* If card is still present we assume the card is properly inserted */
+ if(msc_card_detect(d)) {
+ d->card_present = 1;
+ queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr);
+ }
+
+ return 0;
+}
+
+static void msc_cd_interrupt(msc_drv* d)
+{
+ if(!msc_card_detect(d)) {
+ /* Immediately abort and notify when removing a card */
+ msc_async_abort(d, MSC_REQ_EXTRACTED);
+ if(d->card_present) {
+ d->card_present = 0;
+ queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr);
+ }
+ } else {
+ /* Timer to debounce input */
+ timeout_register(&d->cd_tmo, msc_cd_callback, HZ/4, (intptr_t)d);
+ }
+
+ /* Invert the IRQ */
+ REG_GPIO_PAT0(d->config->cd_gpio.port) ^= d->config->cd_gpio.pin;
+}
+
+void MSC0(void)
+{
+ msc_interrupt(&msc_drivers[0]);
+}
+
+void MSC1(void)
+{
+ msc_interrupt(&msc_drivers[1]);
+}
+
+#ifdef msc0_cd_interrupt
+void msc0_cd_interrupt(void)
+{
+ msc_cd_interrupt(&msc_drivers[0]);
+}
+#endif
+
+#ifdef msc1_cd_interrupt
+void msc1_cd_interrupt(void)
+{
+ msc_cd_interrupt(&msc_drivers[1]);
+}
+#endif
+
+/* ---------------------------------------------------------------------------
+ * SD command helpers
+ */
+
+int msc_cmd_exec(msc_drv* d, msc_req* r)
+{
+ int status = msc_request(d, r);
+ if(status == MSC_REQ_SUCCESS)
+ return status;
+ else if(status == MSC_REQ_LOCKUP || status == MSC_REQ_EXTRACTED)
+ d->driver_flags |= MSC_DF_ERRSTATE;
+ else if(r->flags & (MSC_RF_ERR_CMD12|MSC_RF_AUTO_CMD12)) {
+ /* After an error, the controller does not automatically issue CMD12,
+ * so we need to send it if it's needed, as required by the SD spec.
+ */
+ msc_req nreq = {0};
+ nreq.command = SD_STOP_TRANSMISSION;
+ nreq.resptype = MSC_RESP_R1B;
+ nreq.flags = MSC_RF_ABORT;
+ logf("msc%d: cmd%d error, sending cmd12", d->msc_nr, r->command);
+ if(msc_cmd_exec(d, &nreq))
+ d->driver_flags |= MSC_DF_ERRSTATE;
+ }
+
+ logf("msc%d: err:%d, cmd%d, arg:%x", d->msc_nr, status,
+ r->command, r->argument);
+ return status;
+}
+
+int msc_app_cmd_exec(msc_drv* d, msc_req* r)
+{
+ msc_req areq = {0};
+ areq.command = SD_APP_CMD;
+ areq.argument = d->cardinfo.rca;
+ areq.resptype = MSC_RESP_R1;
+ if(msc_cmd_exec(d, &areq))
+ return areq.status;
+
+ /* Verify that CMD55 was accepted */
+ if((areq.response[0] & (1 << 5)) == 0)
+ return MSC_REQ_ERROR;
+
+ return msc_cmd_exec(d, r);
+}
+
+int msc_cmd_go_idle_state(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_GO_IDLE_STATE;
+ req.resptype = MSC_RESP_NONE;
+ req.flags = MSC_RF_INIT;
+ return msc_cmd_exec(d, &req);
+}
+
+int msc_cmd_send_if_cond(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_SEND_IF_COND;
+ req.argument = 0x1aa;
+ req.resptype = MSC_RESP_R7;
+
+ /* TODO - Check if SEND_IF_COND timeout is really an error
+ * IIRC, this can occur if the card isn't HCS (old cards < 2 GiB).
+ */
+ if(msc_cmd_exec(d, &req))
+ return req.status;
+
+ /* Set HCS bit if the card responds correctly */
+ if((req.response[0] & 0xff) == 0xaa)
+ d->driver_flags |= MSC_DF_HCS_CARD;
+
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_app_op_cond(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_APP_OP_COND;
+ req.argument = 0x00300000; /* 3.4 - 3.6 V */
+ req.resptype = MSC_RESP_R3;
+ if(d->driver_flags & MSC_DF_HCS_CARD)
+ req.argument |= (1 << 30);
+
+ int timeout = 2 * HZ;
+ do {
+ if(msc_app_cmd_exec(d, &req))
+ return req.status;
+ if(req.response[0] & (1 << 31))
+ break;
+ sleep(1);
+ } while(--timeout > 0);
+
+ if(timeout == 0)
+ return MSC_REQ_TIMEOUT;
+
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_all_send_cid(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_ALL_SEND_CID;
+ req.resptype = MSC_RESP_R2;
+ if(msc_cmd_exec(d, &req))
+ return req.status;
+
+ for(int i = 0; i < 4; ++i)
+ d->cardinfo.cid[i] = req.response[i];
+
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_send_rca(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_SEND_RELATIVE_ADDR;
+ req.resptype = MSC_RESP_R6;
+ if(msc_cmd_exec(d, &req))
+ return req.status;
+
+ d->cardinfo.rca = req.response[0] & 0xffff0000;
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_send_csd(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_SEND_CSD;
+ req.argument = d->cardinfo.rca;
+ req.resptype = MSC_RESP_R2;
+ if(msc_cmd_exec(d, &req))
+ return req.status;
+
+ for(int i = 0; i < 4; ++i)
+ d->cardinfo.csd[i] = req.response[i];
+ sd_parse_csd(&d->cardinfo);
+
+ if((req.response[0] >> 30) == 1)
+ d->driver_flags |= MSC_DF_V2_CARD;
+
+ return 0;
+}
+
+int msc_cmd_select_card(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_SELECT_CARD;
+ req.argument = d->cardinfo.rca;
+ req.resptype = MSC_RESP_R1B;
+ return msc_cmd_exec(d, &req);
+}
+
+int msc_cmd_set_bus_width(msc_drv* d, int width)
+{
+ /* TODO - must we check bus width is supported in the cardinfo? */
+ msc_req req = {0};
+ req.command = SD_SET_BUS_WIDTH;
+ req.resptype = MSC_RESP_R1;
+ switch(width) {
+ case 1: req.argument = 0; break;
+ case 4: req.argument = 2; break;
+ default: return MSC_REQ_ERROR;
+ }
+
+ if(msc_app_cmd_exec(d, &req))
+ return req.status;
+
+ msc_set_width(d, width);
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_set_clr_card_detect(msc_drv* d, int arg)
+{
+ msc_req req = {0};
+ req.command = SD_SET_CLR_CARD_DETECT;
+ req.argument = arg;
+ req.resptype = MSC_RESP_R1;
+ return msc_app_cmd_exec(d, &req);
+}
+
+int msc_cmd_switch_freq(msc_drv* d)
+{
+ /* If card doesn't support High Speed, we don't need to send a command */
+ if((d->driver_flags & MSC_DF_V2_CARD) == 0) {
+ msc_set_speed(d, MSC_SPEED_FAST);
+ return MSC_REQ_SUCCESS;
+ }
+
+ /* Try switching to High Speed (50 MHz) */
+ char buffer[64] CACHEALIGN_ATTR;
+ msc_req req = {0};
+ req.command = SD_SWITCH_FUNC;
+ req.argument = 0x80fffff1;
+ req.resptype = MSC_RESP_R1;
+ req.flags = MSC_RF_DATA;
+ req.data = &buffer[0];
+ req.block_len = 64;
+ req.nr_blocks = 1;
+ if(msc_cmd_exec(d, &req))
+ return req.status;
+
+ msc_set_speed(d, MSC_SPEED_HIGH);
+ return MSC_REQ_SUCCESS;
+}
+
+int msc_cmd_send_status(msc_drv* d)
+{
+ msc_req req = {0};
+ req.command = SD_SEND_STATUS;
+ req.argument = d->cardinfo.rca;
+ req.resptype = MSC_RESP_R1;
+ return msc_cmd_exec(d, &req);
+}
+
+int msc_cmd_set_block_len(msc_drv* d, unsigned len)
+{
+ msc_req req = {0};
+ req.command = SD_SET_BLOCKLEN;
+ req.argument = len;
+ req.resptype = MSC_RESP_R1;
+ return msc_cmd_exec(d, &req);
+}
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.h b/firmware/target/mips/ingenic_x1000/msc-x1000.h
new file mode 100644
index 0000000000..53a5b301f0
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.h
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __MSC_X1000_H__
+#define __MSC_X1000_H__
+
+#include "kernel.h"
+#include "sdmmc.h"
+#include <stdbool.h>
+
+/* Number of MSC controllers */
+#define MSC_COUNT 2
+
+/* Media types */
+#define MSC_TYPE_SD 0
+#define MSC_TYPE_MMC 1
+#define MSC_TYPE_ATA 2
+#define MSC_TYPE_ANY 3
+
+/* Clock modes */
+#define MSC_CLK_MANUAL 0
+#define MSC_CLK_AUTOMATIC 1
+
+/* Clock status bits */
+#define MSC_CLKST_ENABLE (1 << 0)
+#define MSC_CLKST_AUTO (1 << 1)
+
+/* Driver flags */
+#define MSC_DF_ERRSTATE (1 << 0)
+#define MSC_DF_READY (1 << 1)
+#define MSC_DF_HCS_CARD (1 << 2)
+#define MSC_DF_V2_CARD (1 << 3)
+
+/* Request status codes */
+#define MSC_REQ_SUCCESS 0
+#define MSC_REQ_CRC_ERR 1
+#define MSC_REQ_CARD_ERR 2
+#define MSC_REQ_TIMEOUT 3
+#define MSC_REQ_EXTRACTED 4
+#define MSC_REQ_LOCKUP 5
+#define MSC_REQ_ERROR 6
+#define MSC_REQ_INCOMPLETE (-1)
+
+/* Response types */
+#define MSC_RESP_NONE 0
+#define MSC_RESP_BUSY (1 << 7)
+#define MSC_RESP_R1 1
+#define MSC_RESP_R1B (MSC_RESP_R1|MSC_RESP_BUSY)
+#define MSC_RESP_R2 2
+#define MSC_RESP_R3 3
+#define MSC_RESP_R6 6
+#define MSC_RESP_R7 7
+
+/* Request flags */
+#define MSC_RF_INIT (1 << 0)
+#define MSC_RF_ERR_CMD12 (1 << 1)
+#define MSC_RF_AUTO_CMD12 (1 << 2)
+#define MSC_RF_PROG (1 << 3)
+#define MSC_RF_DATA (1 << 4)
+#define MSC_RF_WRITE (1 << 5)
+#define MSC_RF_ABORT (1 << 6)
+
+/* Clock speeds */
+#define MSC_SPEED_INIT 400000
+#define MSC_SPEED_FAST 25000000
+#define MSC_SPEED_HIGH 50000000
+
+typedef struct msc_gpio_data {
+ int port;
+ int pin;
+ int active_level;
+} msc_gpio_data;
+
+typedef struct msc_config {
+ int msc_nr;
+ int msc_type;
+ int bus_width;
+ const char* label;
+ struct msc_gpio_data cd_gpio;
+} msc_config;
+
+typedef struct msc_req {
+ /* Filled by caller */
+ int command;
+ unsigned argument;
+ int resptype;
+ int flags;
+ void* data;
+ unsigned nr_blocks;
+ unsigned block_len;
+
+ /* Filled by driver */
+ volatile unsigned response[4];
+ volatile int status;
+} msc_req;
+
+struct sd_dma_desc {
+ unsigned nda;
+ unsigned mem;
+ unsigned len;
+ unsigned cmd;
+} __attribute__((aligned(16)));
+
+typedef struct msc_drv {
+ int msc_nr;
+ int drive_nr;
+ const msc_config* config;
+
+ int driver_flags;
+ int clk_status;
+ unsigned cmdat_def;
+ msc_req* req;
+ unsigned iflag_done;
+
+ volatile int req_running;
+ volatile int card_present;
+
+ struct mutex lock;
+ struct semaphore cmd_done;
+ struct timeout cmd_tmo;
+ struct timeout cd_tmo;
+ struct sd_dma_desc dma_desc;
+
+ tCardInfo cardinfo;
+} msc_drv;
+
+/* Driver initialization, etc */
+extern void msc_init(void);
+extern msc_drv* msc_get(int type, int index);
+extern msc_drv* msc_get_by_drive(int drive_nr);
+
+extern void msc_lock(msc_drv* d);
+extern void msc_unlock(msc_drv* d);
+extern void msc_full_reset(msc_drv* d);
+extern bool msc_card_detect(msc_drv* d);
+
+/* Controller API */
+extern void msc_ctl_reset(msc_drv* d);
+extern void msc_set_clock_mode(msc_drv* d, int mode);
+extern void msc_enable_clock(msc_drv* d, bool enable);
+extern void msc_set_speed(msc_drv* d, int rate);
+extern void msc_set_width(msc_drv* d, int width);
+
+/* Request API */
+extern void msc_async_start(msc_drv* d, msc_req* r);
+extern void msc_async_abort(msc_drv* d, int status);
+extern int msc_async_wait(msc_drv* d, int timeout);
+extern int msc_request(msc_drv* d, msc_req* r);
+
+/* Command helpers; note these are written with SD in mind
+ * and should be reviewed before using them for MMC / CE-ATA
+ */
+extern int msc_cmd_exec(msc_drv* d, msc_req* r);
+extern int msc_app_cmd_exec(msc_drv* d, msc_req* r);
+extern int msc_cmd_go_idle_state(msc_drv* d);
+extern int msc_cmd_send_if_cond(msc_drv* d);
+extern int msc_cmd_app_op_cond(msc_drv* d);
+extern int msc_cmd_all_send_cid(msc_drv* d);
+extern int msc_cmd_send_rca(msc_drv* d);
+extern int msc_cmd_send_csd(msc_drv* d);
+extern int msc_cmd_select_card(msc_drv* d);
+extern int msc_cmd_set_bus_width(msc_drv* d, int width);
+extern int msc_cmd_set_clr_card_detect(msc_drv* d, int arg);
+extern int msc_cmd_switch_freq(msc_drv* d);
+extern int msc_cmd_send_status(msc_drv* d);
+extern int msc_cmd_set_block_len(msc_drv* d, unsigned len);
+
+#endif /* __MSC_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
new file mode 100644
index 0000000000..54a1d11d95
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -0,0 +1,500 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "nand-x1000.h"
+#include "nand-target.h"
+#include "sfc-x1000.h"
+#include "system.h"
+#include <string.h>
+
+#if !defined(NAND_MAX_PAGE_SIZE) || \
+ !defined(NAND_INIT_SFC_DEV_CONF) || \
+ !defined(NAND_INIT_CLOCK_SPEED)
+# error "Target needs to specify NAND driver parameters"
+#endif
+
+/* Must be at least as big as a cacheline */
+#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE
+
+/* Writes have been enabled */
+#define NAND_DRV_FLAG_WRITEABLE 0x01
+
+/* Defined by target */
+extern const nand_chip_desc target_nand_chip_descs[];
+
+/* Globals for the driver
+ * TODO: get rid of pagebuffer in the SPL to save code size
+ */
+static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] CACHEALIGN_ATTR;
+static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] CACHEALIGN_ATTR;
+static nand_drv nand_driver;
+
+static void nand_drv_reset(nand_drv* d)
+{
+ d->chip_ops = NULL;
+ d->chip_data = NULL;
+ d->pagebuf = &pagebuffer[0];
+ d->auxbuf = &auxbuffer[0];
+ d->raw_page_size = 0;
+ d->flags = 0;
+}
+
+/* Driver code */
+int nand_open(void)
+{
+ sfc_init();
+ sfc_lock();
+
+ /* Reset driver state */
+ nand_drv_reset(&nand_driver);
+
+ /* Init hardware */
+ sfc_open();
+ sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF);
+ sfc_set_clock(NAND_CLOCK_SOURCE, NAND_INIT_CLOCK_SPEED);
+
+ /* Identify NAND chip */
+ int status = 0;
+ int nandid = nandcmd_read_id(&nand_driver);
+ if(nandid < 0) {
+ status = -1;
+ goto _err;
+ }
+
+ unsigned char mf_id = nandid >> 8;
+ unsigned char dev_id = nandid & 0xff;
+ const nand_chip_desc* desc = &target_nand_chip_descs[0];
+ while(1) {
+ if(desc->data == NULL || desc->ops == NULL) {
+ status = -1;
+ goto _err;
+ }
+
+ if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id)
+ break;
+ }
+
+ /* Fill driver parameters */
+ nand_driver.chip_ops = desc->ops;
+ nand_driver.chip_data = desc->data;
+ nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size;
+
+ /* Configure hardware and run init op */
+ sfc_set_dev_conf(desc->data->dev_conf);
+ sfc_set_clock(NAND_CLOCK_SOURCE, desc->data->clock_freq);
+
+ if(desc->ops->open(&nand_driver) < 0) {
+ status = -1;
+ goto _err;
+ }
+
+ _exit:
+ sfc_unlock();
+ return status;
+ _err:
+ nand_drv_reset(&nand_driver);
+ sfc_close();
+ goto _exit;
+}
+
+void nand_close(void)
+{
+ sfc_lock();
+ nand_driver.chip_ops->close(&nand_driver);
+ nand_drv_reset(&nand_driver);
+ sfc_close();
+ sfc_unlock();
+}
+
+int nand_enable_writes(bool en)
+{
+ sfc_lock();
+
+ int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en);
+ if(st >= 0) {
+ if(en)
+ nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE;
+ else
+ nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE;
+ }
+
+ sfc_unlock();
+ return st;
+}
+
+extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf)
+{
+ if(count <= 0)
+ return 0;
+
+ nand_drv* d = &nand_driver;
+ uint32_t rowaddr = byteaddr / d->chip_data->page_size;
+ uint32_t coladdr = byteaddr % d->chip_data->page_size;
+ unsigned char* dstbuf = (unsigned char*)buf;
+ int status = 0;
+
+ sfc_lock();
+ do {
+ if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) {
+ status = -1;
+ goto _end;
+ }
+
+ if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) {
+ status = -1;
+ goto _end;
+ }
+
+ int amount = d->chip_data->page_size - coladdr;
+ if(amount > count)
+ amount = count;
+
+ memcpy(dstbuf, d->pagebuf, amount);
+ dstbuf += amount;
+ count -= amount;
+ rowaddr += 1;
+ coladdr = 0;
+ } while(count > 0);
+
+ _end:
+ sfc_unlock();
+ return status;
+}
+
+int nand_write_bytes(uint32_t byteaddr, int count, const void* buf)
+{
+ nand_drv* d = &nand_driver;
+
+ if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0)
+ return -1;
+
+ if(count <= 0)
+ return 0;
+
+ uint32_t rowaddr = byteaddr / d->chip_data->page_size;
+ uint32_t coladdr = byteaddr % d->chip_data->page_size;
+
+ /* Only support whole page writes right now */
+ if(coladdr != 0)
+ return -1;
+ if(count % d->chip_data->page_size)
+ return -1;
+
+ const unsigned char* srcbuf = (const unsigned char*)buf;
+ int status = 0;
+
+ sfc_lock();
+ do {
+ memcpy(d->pagebuf, srcbuf, d->chip_data->page_size);
+ d->chip_ops->ecc_write(d, d->pagebuf);
+
+ if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) {
+ status = -1;
+ goto _end;
+ }
+
+ rowaddr += 1;
+ srcbuf += d->chip_data->page_size;
+ count -= d->chip_data->page_size;
+ } while(count > 0);
+
+ _end:
+ sfc_unlock();
+ return status;
+}
+
+int nand_erase_block(uint32_t byteaddr)
+{
+ nand_drv* d = &nand_driver;
+
+ if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0)
+ return -1;
+
+ /* Ensure address is aligned to a block boundary */
+ uint32_t blockaddr = byteaddr / d->chip_data->page_size;
+ if(blockaddr % d->chip_data->block_size)
+ return -1;
+
+ int status = 0;
+ sfc_lock();
+
+ if(d->chip_ops->erase_block(d, blockaddr)) {
+ status = -1;
+ goto _end;
+ }
+
+ _end:
+ sfc_unlock();
+ return status;
+}
+
+int nandcmd_read_id(nand_drv* d)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_READ_ID;
+ op.flags = SFC_FLAG_READ;
+ op.addr_bytes = 1;
+ op.addr_lo = 0;
+ op.data_bytes = 2;
+ op.buffer = d->auxbuf;
+ if(sfc_exec(&op))
+ return -1;
+
+ return (d->auxbuf[0] << 8) | d->auxbuf[1];
+}
+
+int nandcmd_write_enable(nand_drv* d)
+{
+ (void)d;
+
+ sfc_op op = {0};
+ op.command = NAND_CMD_WRITE_ENABLE;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_get_feature(nand_drv* d, int reg)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_GET_FEATURE;
+ op.flags = SFC_FLAG_READ;
+ op.addr_bytes = 1;
+ op.addr_lo = reg & 0xff;
+ op.data_bytes = 1;
+ op.buffer = d->auxbuf;
+ if(sfc_exec(&op))
+ return -1;
+
+ return d->auxbuf[0];
+}
+
+int nandcmd_set_feature(nand_drv* d, int reg, int val)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_SET_FEATURE;
+ op.flags = SFC_FLAG_READ;
+ op.addr_bytes = 1;
+ op.addr_lo = reg & 0xff;
+ op.data_bytes = 1;
+ op.buffer = d->auxbuf;
+ d->auxbuf[0] = val & 0xff;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_PAGE_READ_TO_CACHE;
+ op.addr_bytes = d->chip_data->rowaddr_width;
+ op.addr_lo = rowaddr;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf)
+{
+ sfc_op op = {0};
+ if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) {
+ op.command = NAND_CMD_READ_FROM_CACHEx4;
+ op.mode = SFC_MODE_QUAD_IO;
+ } else {
+ op.command = NAND_CMD_READ_FROM_CACHE;
+ op.mode = SFC_MODE_STANDARD;
+ }
+
+ op.flags = SFC_FLAG_READ;
+ op.addr_bytes = d->chip_data->coladdr_width;
+ op.addr_lo = 0;
+ op.dummy_bits = 8;
+ op.data_bytes = d->raw_page_size;
+ op.buffer = buf;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_program_load(nand_drv* d, const unsigned char* buf)
+{
+ sfc_op op = {0};
+ if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) {
+ op.command = NAND_CMD_PROGRAM_LOADx4;
+ op.mode = SFC_MODE_QUAD_IO;
+ } else {
+ op.command = NAND_CMD_PROGRAM_LOAD;
+ op.mode = SFC_MODE_STANDARD;
+ }
+
+ op.flags = SFC_FLAG_WRITE;
+ op.addr_bytes = d->chip_data->coladdr_width;
+ op.addr_lo = 0;
+ op.data_bytes = d->raw_page_size;
+ op.buffer = (void*)buf;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_PROGRAM_EXECUTE;
+ op.addr_bytes = d->chip_data->rowaddr_width;
+ op.addr_lo = rowaddr;
+ if(sfc_exec(&op))
+ return -1;
+
+ return 0;
+}
+
+int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr)
+{
+ sfc_op op = {0};
+ op.command = NAND_CMD_BLOCK_ERASE;
+ op.addr_bytes = d->chip_data->rowaddr_width;
+ op.addr_lo = blockaddr;
+ if(sfc_exec(&op))
+ return 01;
+
+ return 0;
+}
+
+const nand_chip_ops nand_chip_ops_std = {
+ .open = nandop_std_open,
+ .close = nandop_std_close,
+ .read_page = nandop_std_read_page,
+ .write_page = nandop_std_write_page,
+ .erase_block = nandop_std_erase_block,
+ .set_wp_enable = nandop_std_set_wp_enable,
+ .ecc_read = nandop_ecc_none_read,
+ .ecc_write = nandop_ecc_none_write,
+};
+
+/* Helper needed by other ops */
+static int nandop_std_wait_status(nand_drv* d, int errbit)
+{
+ int reg;
+ do {
+ reg = nandcmd_get_feature(d, NAND_FREG_STATUS);
+ if(reg < 0)
+ return -1;
+ } while(reg & NAND_FREG_STATUS_OIP);
+
+ if(reg & errbit)
+ return -1;
+
+ return reg;
+}
+
+int nandop_std_open(nand_drv* d)
+{
+ (void)d;
+ return 0;
+}
+
+void nandop_std_close(nand_drv* d)
+{
+ (void)d;
+}
+
+int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf)
+{
+ if(nandcmd_page_read_to_cache(d, rowaddr) < 0)
+ return -1;
+ if(nandop_std_wait_status(d, 0) < 0)
+ return -1;
+ if(nandcmd_read_from_cache(d, buf) < 0)
+ return -1;
+
+ return 0;
+}
+
+int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf)
+{
+ if(nandcmd_write_enable(d) < 0)
+ return -1;
+ if(nandcmd_program_load(d, buf) < 0)
+ return -1;
+ if(nandcmd_program_execute(d, rowaddr) < 0)
+ return -1;
+ if(nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL) < 0)
+ return -1;
+
+ return 0;
+}
+
+int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr)
+{
+ if(nandcmd_write_enable(d) < 0)
+ return -1;
+ if(nandcmd_block_erase(d, blockaddr) < 0)
+ return -1;
+ if(nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0)
+ return -1;
+
+ return 0;
+}
+
+int nandop_std_set_wp_enable(nand_drv* d, bool en)
+{
+ int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION);
+ if(val < 0)
+ return -1;
+
+ if(en) {
+ val &= ~NAND_FREG_PROTECTION_ALLBP;
+ if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
+ val &= ~NAND_FREG_PROTECTION_BRWD;
+ } else {
+ val |= NAND_FREG_PROTECTION_ALLBP;
+ if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
+ val |= NAND_FREG_PROTECTION_BRWD;
+ }
+
+ sfc_set_wp_enable(false);
+ int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val);
+ sfc_set_wp_enable(true);
+
+ if(status < 0)
+ return -1;
+
+ return 0;
+}
+
+int nandop_ecc_none_read(nand_drv* d, unsigned char* buf)
+{
+ (void)d;
+ (void)buf;
+ return 0;
+}
+
+void nandop_ecc_none_write(nand_drv* d, unsigned char* buf)
+{
+ memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size);
+}
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
new file mode 100644
index 0000000000..865feb38c5
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __NAND_X1000_H__
+#define __NAND_X1000_H__
+
+/* NOTE: this is a very minimal API designed only to support a bootloader.
+ * Not suitable for general data storage. It doesn't have proper support for
+ * partial page writes, access to spare area, etc, which are all necessary
+ * for an effective flash translation layer.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* Chip supports quad I/O for page read/write */
+#define NANDCHIP_FLAG_QUAD 0x01
+
+/* Set/clear the BRWD bit when enabling/disabling write protection */
+#define NANDCHIP_FLAG_USE_BRWD 0x02
+
+typedef struct nand_drv nand_drv;
+
+/* Defines some static information about a NAND chip */
+typedef struct nand_chip_data {
+ const char* name; /* Name for debugging purposes */
+ uint8_t mf_id; /* Manufacturer ID */
+ uint8_t dev_id; /* Device ID */
+ uint8_t rowaddr_width; /* Number of bytes in row addresses */
+ uint8_t coladdr_width; /* Number of bytes in column addresses */
+ uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */
+ uint32_t clock_freq; /* Frequency to switch to after identification */
+ uint32_t block_size; /* Number of pages per eraseblock */
+ uint32_t page_size; /* Number of data bytes per page */
+ uint32_t spare_size; /* Number of spare bytes per page */
+ int flags; /* Various flags */
+} nand_chip_data;
+
+/* Defines high-level operations used to implement the public API.
+ * Chips may need to override operations if the default ones aren't suitable.
+ *
+ * Negative return codes return an error, while zero or positive codes are
+ * considered successful. This allows a function to return meaningful data,
+ * if applicable.
+ */
+typedef struct nand_chip_ops {
+ /* Called once after identifying the chip */
+ int(*open)(nand_drv* d);
+
+ /* Called once when the driver is closed */
+ void(*close)(nand_drv* d);
+
+ /* Read or write a complete page including both main and spare areas. */
+ int(*read_page)(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
+ int(*write_page)(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
+
+ /* Erase a block. */
+ int(*erase_block)(nand_drv* d, uint32_t blockaddr);
+
+ /* Enable or disable the chip's write protection. */
+ int(*set_wp_enable)(nand_drv* d, bool en);
+
+ /* Perform error correction and detection on a raw page (main + spare).
+ * Return the number of errors detected and corrected, or a negative value
+ * if errors were detected but could not be corrected.
+ */
+ int(*ecc_read)(nand_drv* d, unsigned char* buf);
+
+ /* Generate ECC data for a page. The buffer main area is already filled
+ * and this function should write ECC data into the spare area.
+ */
+ void(*ecc_write)(nand_drv* d, unsigned char* buf);
+} nand_chip_ops;
+
+/* Struct used to list all supported NAND chips in an array */
+typedef struct nand_chip_desc {
+ const nand_chip_data* data;
+ const nand_chip_ops* ops;
+} nand_chip_desc;
+
+/* NAND driver structure. It can be accessed by chip ops, but they must not
+ * modify any fields except for "auxbuf", which is a small buffer that can
+ * be used for commands that need to read/write small amounts of data: often
+ * needed for polling status, etc.
+ */
+struct nand_drv {
+ const nand_chip_ops* chip_ops;
+ const nand_chip_data* chip_data;
+ unsigned char* pagebuf;
+ unsigned char* auxbuf;
+ uint32_t raw_page_size;
+ int flags;
+};
+
+/* Note: sfc_init() must be called prior to nand_open() */
+extern int nand_open(void);
+extern void nand_close(void);
+
+/* Controls device-side write protection registers as well as software lock.
+ * Erase and program operations will fail unless you first enable writes.
+ */
+extern int nand_enable_writes(bool en);
+
+/* Byte-based NAND operations */
+extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf);
+extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf);
+extern int nand_erase_block(uint32_t byteaddr);
+
+/* NAND command numbers */
+#define NAND_CMD_READ_ID 0x9f
+#define NAND_CMD_WRITE_ENABLE 0x06
+#define NAND_CMD_GET_FEATURE 0x0f
+#define NAND_CMD_SET_FEATURE 0x1f
+#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
+#define NAND_CMD_READ_FROM_CACHE 0x0b
+#define NAND_CMD_READ_FROM_CACHEx4 0x6b
+#define NAND_CMD_PROGRAM_LOAD 0x02
+#define NAND_CMD_PROGRAM_LOADx4 0x32
+#define NAND_CMD_PROGRAM_EXECUTE 0x10
+#define NAND_CMD_BLOCK_ERASE 0xd8
+
+/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
+#define NAND_FREG_PROTECTION 0xa0
+#define NAND_FREG_FEATURE 0xb0
+#define NAND_FREG_STATUS 0xc0
+
+/* Protection register bits */
+#define NAND_FREG_PROTECTION_BRWD 0x80
+#define NAND_FREG_PROTECTION_BP2 0x20
+#define NAND_FREG_PROTECTION_BP1 0x10
+#define NAND_FREG_PROTECTION_BP0 0x80
+/* Mask of BP bits 0-2 */
+#define NAND_FREG_PROTECTION_ALLBP (0x38)
+
+/* Feature register bits */
+#define NAND_FREG_FEATURE_QE 0x01
+
+/* Status register bits */
+#define NAND_FREG_STATUS_OIP 0x01
+#define NAND_FREG_STATUS_WEL 0x02
+#define NAND_FREG_STATUS_E_FAIL 0x04
+#define NAND_FREG_STATUS_P_FAIL 0x08
+
+/* Standard implementations for low-level NAND commands. I'm not aware of any
+ * actual standard governing these, but it seems many vendors follow the same
+ * command numbering, status bits, and behavior so these implementations should
+ * work across a wide variety of chips.
+ *
+ * If adding a new NAND chip which only has a minor deviation from these
+ * standard implementations, consider adding a flag and modifying these
+ * functions to change their behavior based on the flag, instead of writing
+ * a whole new implementation.
+ *
+ * None of these functions are directly called by the high-level driver code,
+ * except for nandcmd_std_read_id(). They can be used to implement higher-level
+ * functions in a device's "nand_chip_ops".
+ */
+extern int nandcmd_read_id(nand_drv* d);
+extern int nandcmd_write_enable(nand_drv* d);
+extern int nandcmd_get_feature(nand_drv* d, int reg);
+extern int nandcmd_set_feature(nand_drv* d, int reg, int val);
+extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr);
+extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf);
+extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf);
+extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr);
+extern int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr);
+
+/* Table filled with all the standard operations for chips which don't
+ * need to override any operations.
+ */
+extern const nand_chip_ops nand_chip_ops_std;
+
+/* Standard NAND chip ops based on the standard "nandcmd" functions.
+ *
+ * Same advice applies here as there: if it's possible to support minor
+ * chip variations with a flag, modify these functions to do so.
+ */
+extern int nandop_std_open(nand_drv* d);
+extern void nandop_std_close(nand_drv* d);
+extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
+extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
+extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr);
+extern int nandop_std_set_wp_enable(nand_drv* d, bool en);
+
+/* The default ECC implementation is a no-op: reads always succeed without
+ * reporting errors and writes will fill the spare area with '0xff' bytes.
+ *
+ * For chips that support internal ECC, this often works because the chip will
+ * ignore writes to the ECC areas.
+ */
+extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf);
+extern void nandop_ecc_none_write(nand_drv* d, unsigned char* buf);
+
+#endif /* __NAND_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
new file mode 100644
index 0000000000..9ae6f5a956
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "kernel.h"
+#include "audiohw.h"
+#include "pcm.h"
+#include "pcm-internal.h"
+#include "panic.h"
+#include "dma-x1000.h"
+#include "irq-x1000.h"
+#include "x1000/aic.h"
+
+#define AIC_STATE_STOPPED 0
+#define AIC_STATE_PLAYING 1
+
+volatile unsigned aic_tx_underruns = 0;
+
+static int aic_state = AIC_STATE_STOPPED;
+
+static int aic_lock = 0;
+static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
+
+static dma_desc aic_dma_desc;
+
+static void pcm_dma_int_cb(int event);
+
+void pcm_play_dma_init(void)
+{
+ /* Let the target initialize its hardware and setup the AIC */
+ audiohw_init();
+
+ /* Set DMA callback */
+ dma_set_callback(DMA_CHANNEL_AUDIO, pcm_dma_int_cb);
+
+ /* Program FIFO threshold -- DMA settings must match */
+ jz_writef(AIC_CFG, TFTH(16));
+
+ /* Ensure all playback is disabled */
+ jz_writef(AIC_CCR, ERPL(0));
+
+ /* Enable the controller */
+ jz_writef(AIC_CFG, ENABLE(1));
+
+ /* Enable interrupts */
+ system_enable_irq(IRQ_AIC);
+}
+
+void pcm_play_dma_postinit(void)
+{
+ audiohw_postinit();
+}
+
+void pcm_dma_apply_settings(void)
+{
+ audiohw_set_frequency(pcm_fsel);
+}
+
+static void pcm_dma_start(const void* addr, size_t size)
+{
+ aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ aic_dma_desc.sa = PHYSADDR(addr);
+ aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
+ aic_dma_desc.tc = size;
+ aic_dma_desc.sd = 0;
+ aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
+ aic_dma_desc.pad0 = 0;
+ aic_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
+ commit_dcache_range(addr, size);
+
+ REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
+
+ pcm_play_dma_status_callback(PCM_DMAST_STARTED);
+}
+
+static void pcm_dma_handle_event(int event)
+{
+ if(event == DMA_EVENT_COMPLETE) {
+ const void* addr;
+ size_t size;
+ if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
+ pcm_dma_start(addr, size);
+ } else if(event == DMA_EVENT_NONE) {
+ /* ignored, so callers don't need to check for this */
+ } else {
+ pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA);
+ }
+}
+
+static void pcm_dma_int_cb(int event)
+{
+ if(aic_lock) {
+ aic_dma_pending_event = event;
+ return;
+ } else {
+ pcm_dma_handle_event(event);
+ }
+}
+
+void pcm_play_dma_start(const void* addr, size_t size)
+{
+ aic_dma_pending_event = DMA_EVENT_NONE;
+ aic_state = AIC_STATE_PLAYING;
+
+ pcm_dma_start(addr, size);
+ jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1));
+}
+
+void pcm_play_dma_stop(void)
+{
+ jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
+ jz_writef(AIC_CCR, TFLUSH(1));
+
+ aic_dma_pending_event = DMA_EVENT_NONE;
+ aic_state = AIC_STATE_STOPPED;
+}
+
+void pcm_play_lock(void)
+{
+ ++aic_lock;
+}
+
+void pcm_play_unlock(void)
+{
+ int irq = disable_irq_save();
+ if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
+ pcm_dma_handle_event(aic_dma_pending_event);
+ aic_dma_pending_event = DMA_EVENT_NONE;
+ }
+
+ restore_irq(irq);
+}
+
+void AIC(void)
+{
+ if(jz_readf(AIC_SR, TUR)) {
+ aic_tx_underruns += 1;
+ jz_writef(AIC_SR, TUR(0));
+ }
+}
diff --git a/firmware/target/mips/ingenic_x1000/pwm-x1000.c b/firmware/target/mips/ingenic_x1000/pwm-x1000.c
new file mode 100644
index 0000000000..37d2856c1a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/pwm-x1000.c
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "pwm-x1000.h"
+#include "clk-x1000.h"
+#include "gpio-x1000.h"
+#include "system.h"
+#include "kernel.h"
+#include "x1000/tcu.h"
+
+struct pwm_gpio_data {
+ int port;
+ unsigned pin;
+ int func;
+};
+
+struct pwm_state {
+ struct pwm_gpio_data gpio;
+ int period_ns;
+ int duty_ns;
+ int full_ticks;
+ int half_ticks;
+ int prescaler;
+};
+
+static struct pwm_state pwm_state[] = {
+ {{GPIO_C, 1 << 25, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1},
+ {{GPIO_C, 1 << 26, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1},
+ {{GPIO_C, 1 << 27, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1},
+ {{GPIO_B, 1 << 6, GPIO_DEVICE(2)}, -1, -1, -1, -1, -1},
+ {{GPIO_C, 1 << 24, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1},
+};
+
+void pwm_init(int chn)
+{
+ /* clear cached state */
+ struct pwm_state* st = &pwm_state[chn];
+ st->period_ns = -1;
+ st->duty_ns = -1;
+ st->full_ticks = -1;
+ st->prescaler = -1;
+ st->prescaler = -1;
+
+ /* clear GPIO and disable timer */
+ gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0));
+ jz_clr(TCU_STOP, 1 << chn);
+ jz_clr(TCU_ENABLE, 1 << chn);
+ jz_set(TCU_STOP, 1 << chn);
+}
+
+void pwm_set_period(int chn, int period_ns, int duty_ns)
+{
+ struct pwm_state* st = &pwm_state[chn];
+ unsigned long long tmp;
+ int full_ticks = st->full_ticks;
+ int half_ticks = st->half_ticks;
+ int prescaler = st->prescaler;
+
+ if(period_ns != st->period_ns) {
+ /* calculate full tick period and prescaler */
+ tmp = clk_get(X1000_CLK_PCLK) / 1000000;
+ tmp *= period_ns;
+ tmp /= 1000;
+
+ prescaler = 0;
+ while(tmp > 0xffff && prescaler < 5) {
+ tmp /= 4;
+ prescaler += 1;
+ }
+
+ full_ticks = (tmp > 0xffff) ? 0xffff : tmp;
+ st->period_ns = period_ns;
+ }
+
+ if(duty_ns != st->duty_ns) {
+ /* calculate half tick value */
+ tmp = full_ticks;
+ tmp *= duty_ns;
+ tmp /= period_ns;
+
+ half_ticks = (tmp > 0xffff) ? 0xffff : tmp;
+ if(half_ticks >= full_ticks)
+ half_ticks = full_ticks - 1;
+ st->duty_ns = duty_ns;
+ }
+
+ /* need to clear STOP bit to access timer unit registers */
+ int was_stopped = !!(jz_read(TCU_STOP) & (1 << chn));
+ if(was_stopped)
+ jz_clr(TCU_STOP, 1 << chn);
+
+ /* check if timer is currently running */
+ int was_enabled = !!(jz_read(TCU_ENABLE) & (1 << chn));
+ int enabled = was_enabled;
+
+ if(prescaler != st->prescaler) {
+ /* must disable timer to change these settings */
+ if(was_enabled) {
+ jz_clr(TCU_ENABLE, 1 << chn);
+ enabled = 0;
+ }
+
+ jz_overwritef(TCU_CTRL(chn), SHUTDOWN_V(GRACEFUL), INIT_LVL(0),
+ PWM_EN(1), PRESCALE(prescaler), SOURCE_V(PCLK));
+ REG_TCU_COUNT(chn) = 0;
+ st->prescaler = prescaler;
+ }
+
+ if(full_ticks != st->full_ticks || half_ticks != st->half_ticks) {
+ if(enabled) {
+ /* avoid changing PWM settings in the middle of a cycle */
+ unsigned cmp = REG_TCU_CMP_FULL(chn) - 1;
+ long deadline = current_tick + 3;
+ while(REG_TCU_COUNT(chn) < cmp
+ && TIME_BEFORE(current_tick, deadline));
+ }
+
+ /* these can be changed while the timer is running */
+ REG_TCU_CMP_FULL(chn) = full_ticks;
+ REG_TCU_CMP_HALF(chn) = full_ticks - half_ticks;
+ st->full_ticks = full_ticks;
+ st->half_ticks = half_ticks;
+ }
+
+ /* restore the enable/stop state */
+ if(was_enabled && !enabled)
+ jz_set(TCU_ENABLE, 1 << chn);
+ if(was_stopped)
+ jz_set(TCU_STOP, 1 << chn);
+}
+
+void pwm_enable(int chn)
+{
+ /* Start timer */
+ jz_clr(TCU_STOP, 1 << chn);
+ jz_set(TCU_ENABLE, 1 << chn);
+
+ /* Configure GPIO function */
+ struct pwm_state* st = &pwm_state[chn];
+ gpio_config(st->gpio.port, st->gpio.pin, st->gpio.func);
+}
+
+void pwm_disable(int chn)
+{
+ /* Set GPIO to output 0 */
+ struct pwm_state* st = &pwm_state[chn];
+ gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0));
+
+ /* Stop timer */
+ jz_clr(TCU_ENABLE, 1 << chn);
+ jz_set(TCU_STOP, 1 << chn);
+}
diff --git a/firmware/target/mips/ingenic_x1000/pwm-x1000.h b/firmware/target/mips/ingenic_x1000/pwm-x1000.h
new file mode 100644
index 0000000000..0cea266d63
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/pwm-x1000.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __PWM_X1000_H__
+#define __PWM_X1000_H__
+
+/* Usage:
+ * - There are 5 PWM channels (0-4) corresponding to TCUs 0-4
+ * - Call pwm_init(n) before using channel n
+ * - Call pwm_set_period() to change the period and duty cycle at any time
+ * - Call pwm_enable() and pwm_disable() to turn the output on and off
+ * - Don't allow two threads to control the same channel at the same time
+ * - Don't call pwm_init(), pwm_enable(), or pwm_disable() from an interrupt
+ *
+ * After calling pwm_init(), the channel is essentially in a disabled state so
+ * you will need to call pwm_set_period() and then pwm_enable() to turn it on.
+ * Don't alter the channel's TCU or GPIO pin state after calling pwm_init().
+ *
+ * After calling pwm_disable(), it is safe to use the channel's TCU or GPIO pin
+ * for some other purpose, but you must call pwm_init() before you can use the
+ * channel as PWM output again.
+ */
+
+extern void pwm_init(int chn);
+extern void pwm_set_period(int chn, int period_ns, int duty_ns);
+extern void pwm_enable(int chn);
+extern void pwm_disable(int chn);
+
+#endif /* __PWM_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/sd-x1000.c b/firmware/target/mips/ingenic_x1000/sd-x1000.c
new file mode 100644
index 0000000000..7fba617ce3
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/sd-x1000.c
@@ -0,0 +1,236 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "storage.h"
+#include "sdmmc.h"
+#include "sd.h"
+#include "msc-x1000.h"
+#include <string.h>
+
+/* #define LOGF_ENABLE */
+#include "logf.h"
+
+static msc_drv* sd_to_msc[MSC_COUNT];
+static long _sd_last_disk_activity = 0;
+
+static int sd_init_card(msc_drv* d)
+{
+ int s;
+ if(s = msc_cmd_go_idle_state(d)) return -100 - s;
+ if(s = msc_cmd_send_if_cond(d)) return -110 - s;
+ if(s = msc_cmd_app_op_cond(d)) return -120 - s;
+ if(s = msc_cmd_all_send_cid(d)) return -130 - s;
+ if(s = msc_cmd_send_rca(d)) return -140 - s;
+ if(s = msc_cmd_send_csd(d)) return -150 - s;
+ if(s = msc_cmd_select_card(d)) return -160 - s;
+ if(s = msc_cmd_set_clr_card_detect(d, 0)) return -170 - s;
+ if(s = msc_cmd_set_bus_width(d, 4)) return -180 - s;
+ if(s = msc_cmd_switch_freq(d)) return -190 - s;
+ d->driver_flags |= MSC_DF_READY;
+ d->cardinfo.initialized = 1;
+ return 0;
+}
+
+static int sd_transfer(msc_drv* d, bool write,
+ unsigned long start, int count, void* buf)
+{
+ int status = -1;
+
+ msc_lock(d);
+ if(!d->card_present)
+ goto _exit;
+
+ /* Hopefully puts the driver into a working state */
+ if(d->driver_flags & MSC_DF_ERRSTATE) {
+ logf("MSC%d: attempting to reset after ERRSTATE", d->msc_nr);
+ msc_full_reset(d);
+ }
+
+ /* Init card if needed */
+ if((d->driver_flags & MSC_DF_READY) == 0) {
+ if(status = sd_init_card(d)) {
+ logf("MSC%d: card init failed (code %d)", d->msc_nr, status);
+ d->driver_flags |= MSC_DF_ERRSTATE;
+ d->cardinfo.initialized = status;
+ goto _exit;
+ }
+ }
+
+ /* Ensure parameters are within range */
+ if(count < 1)
+ goto _exit;
+ if(start + count > d->cardinfo.numblocks)
+ goto _exit;
+
+ do {
+ /* We can only do 65536 blocks at a time */
+ int xfer_count = count > 0xffff ? 0xffff : count;
+
+ /* Set block length. I think this is only necessary for non-HCS cards.
+ * HCS cards always use 512 bytes so we shouldn't need it.
+ */
+ if((d->driver_flags & MSC_DF_HCS_CARD) == 0)
+ if(status = msc_cmd_set_block_len(d, SD_BLOCK_SIZE))
+ goto _exit;
+
+ /* TODO - look into using CMD23 to improve transfer performance.
+ * This specifies the number of blocks ahead of time, instead of
+ * relying on CMD12 to stop transmission. CMD12 is still needed
+ * in the event of errors though.
+ */
+ msc_req req = {0};
+ req.data = buf;
+ req.nr_blocks = xfer_count;
+ req.block_len = SD_BLOCK_SIZE;
+ req.resptype = MSC_RESP_R1;
+ req.flags = MSC_RF_DATA;
+ if(xfer_count > 1)
+ req.flags |= MSC_RF_AUTO_CMD12;
+ if(write) {
+ req.command = xfer_count == 1 ? SD_WRITE_BLOCK
+ : SD_WRITE_MULTIPLE_BLOCK;
+ req.flags |= MSC_RF_PROG | MSC_RF_WRITE;
+ } else {
+ req.command = xfer_count == 1 ? SD_READ_SINGLE_BLOCK
+ : SD_READ_MULTIPLE_BLOCK;
+ }
+
+ if(d->driver_flags & MSC_DF_V2_CARD)
+ req.argument = start;
+ else
+ req.argument = start * SD_BLOCK_SIZE;
+
+ if(status = msc_cmd_exec(d, &req))
+ goto _exit;
+
+ /* TODO - properly handle reading the last block of the SD card
+ * This is likely to fail if we're reading near the end because
+ * the SD card will try to read past the last sector and then
+ * signal an error. So we need to ignore that error, but only if
+ * it was expected to occur. (See SD spec sec. 4.3.3, "Block Read")
+ */
+ if(status = msc_cmd_send_status(d))
+ goto _exit;
+
+ /* Advance the buffer and adjust start/count */
+ buf += xfer_count * SD_BLOCK_SIZE;
+ start += xfer_count;
+ count -= xfer_count;
+ } while(count > 0);
+
+ _exit:
+ msc_unlock(d);
+ return status;
+}
+
+int sd_read_sectors(IF_MD(int drive,) unsigned long start,
+ int count, void* buf)
+{
+ return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false,
+ start, count, buf);
+}
+
+int sd_write_sectors(IF_MD(int drive,) unsigned long start,
+ int count, const void* buf)
+{
+ return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true,
+ start, count, (void*)buf);
+}
+
+tCardInfo* card_get_info_target(int card_nr)
+{
+ /* Defensive measures */
+ if(card_nr < 0 || card_nr >= MSC_COUNT || sd_to_msc[card_nr] == NULL) {
+ static tCardInfo null_info = { 0 };
+ return &null_info;
+ }
+
+ return &sd_to_msc[card_nr]->cardinfo;
+}
+
+int sd_event(long id, intptr_t data)
+{
+ if(id == SYS_HOTSWAP_EXTRACTED) {
+ msc_drv* d = msc_get_by_drive(data);
+ if(d)
+ msc_full_reset(d);
+ return 0;
+ } else {
+ return storage_event_default_handler(id, data, _sd_last_disk_activity,
+ STORAGE_SD);
+ }
+}
+
+long sd_last_disk_activity(void)
+{
+ return _sd_last_disk_activity;
+}
+
+bool sd_present(IF_MD_NONVOID(int drive))
+{
+ /* Seems that volume_properties() in firmware/common/disk.c may pass
+ * drive = -1 when the SD card is not inserted, so just return false.
+ */
+ if(drive < 0)
+ return false;
+
+ return sd_to_msc[IF_MD_DRV(drive)]->card_present;
+}
+
+bool sd_removable(IF_MD_NONVOID(int drive))
+{
+ /* Same reason as sd_present() */
+ if(drive < 0)
+ return false;
+
+ return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio.pin != 0;
+}
+
+#ifndef CONFIG_STORAGE_MULTI
+static
+#endif
+int sd_num_drives(int first_drive)
+{
+ int n = 0;
+ for(; n < MSC_COUNT; ++n) {
+ msc_drv* d = msc_get(MSC_TYPE_SD, n);
+ if(d == NULL)
+ break;
+
+ d->drive_nr = first_drive + n;
+ sd_to_msc[n] = d;
+ }
+
+ for(int i = n; i < MSC_COUNT; ++i)
+ sd_to_msc[i] = NULL;
+
+ return n;
+}
+
+int sd_init(void)
+{
+ msc_init();
+#ifndef CONFIG_STORAGE_MULTI
+ sd_num_drives(0);
+#endif
+
+ return 0;
+}
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
new file mode 100644
index 0000000000..9537cdc035
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
@@ -0,0 +1,298 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "kernel.h"
+#include "panic.h"
+#include "sfc-x1000.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "x1000/sfc.h"
+#include "x1000/cpm.h"
+
+#ifndef BOOTLOADER_SPL
+/* DMA only works once the system is properly booted */
+# define NEED_SFC_DMA
+#endif
+
+#if defined(BOOTLOADER_SPL)
+# if X1000_EXCLK_FREQ == 24000000
+# define FIXED_CLK_FREQ 600000000
+# define FIXED_CLK_SRC X1000_CLK_MPLL
+# elif X1000_EXCLK_FREQ == 26000000
+# define FIXED_CLK_FREQ 598000000
+# define FIXED_CLK_SRC X1000_CLK_MPLL
+# else
+# error "bad EXCLK freq"
+# endif
+#endif
+
+#define FIFO_THRESH 31
+
+#define SFC_STATUS_PENDING (-1)
+
+#ifdef NEED_SFC_DMA
+static struct mutex sfc_mutex;
+static struct semaphore sfc_sema;
+static struct timeout sfc_lockup_tmo;
+static bool sfc_inited = false;
+static volatile int sfc_status;
+#else
+# define sfc_status SFC_STATUS_OK
+#endif
+
+void sfc_init(void)
+{
+#ifdef NEED_SFC_DMA
+ if(sfc_inited)
+ return;
+
+ mutex_init(&sfc_mutex);
+ semaphore_init(&sfc_sema, 1, 0);
+ sfc_inited = true;
+#endif
+}
+
+void sfc_lock(void)
+{
+#ifdef NEED_SFC_DMA
+ mutex_lock(&sfc_mutex);
+#endif
+}
+
+void sfc_unlock(void)
+{
+#ifdef NEED_SFC_DMA
+ mutex_unlock(&sfc_mutex);
+#endif
+}
+
+void sfc_open(void)
+{
+ gpio_config(GPIO_A, 0x3f << 26, GPIO_DEVICE(1));
+ jz_writef(CPM_CLKGR, SFC(0));
+ jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1),
+ THRESHOLD(FIFO_THRESH), WP_EN(1));
+ REG_SFC_CGE = 0;
+ REG_SFC_INTC = 0x1f;
+ REG_SFC_MEM_ADDR = 0;
+
+#ifdef NEED_SFC_DMA
+ jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32));
+ system_enable_irq(IRQ_SFC);
+#endif
+}
+
+void sfc_close(void)
+{
+#ifdef NEED_SFC_DMA
+ system_disable_irq(IRQ_SFC);
+#endif
+
+ REG_SFC_CGE = 0x1f;
+ jz_writef(CPM_CLKGR, SFC(1));
+}
+
+void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq)
+{
+ uint32_t in_freq;
+#ifdef FIXED_CLK_FREQ
+ /* Small optimization to save code space in SPL by not polling clock */
+ clksrc = FIXED_CLK_SRC;
+ in_freq = FIXED_CLK_FREQ;
+#else
+ in_freq = clk_get(clksrc);
+#endif
+
+ uint32_t div = clk_calc_div(in_freq, freq);
+ jz_writef(CPM_SSICDR, CE(1), CLKDIV(div - 1),
+ SFC_CS(clksrc == X1000_CLK_MPLL ? 1 : 0));
+ while(jz_readf(CPM_SSICDR, BUSY));
+ jz_writef(CPM_SSICDR, CE(0));
+}
+
+#ifdef NEED_SFC_DMA
+static int sfc_lockup_tmo_cb(struct timeout* tmo)
+{
+ (void)tmo;
+
+ int irq = disable_irq_save();
+ if(sfc_status == SFC_STATUS_PENDING) {
+ sfc_status = SFC_STATUS_LOCKUP;
+ jz_overwritef(SFC_TRIG, STOP(1));
+ semaphore_release(&sfc_sema);
+ }
+
+ restore_irq(irq);
+ return 0;
+}
+
+static void sfc_wait_end(void)
+{
+ semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
+}
+
+void SFC(void)
+{
+ unsigned sr = REG_SFC_SR & ~REG_SFC_INTC;
+
+ if(jz_vreadf(sr, SFC_SR, OVER)) {
+ jz_overwritef(SFC_SCR, CLR_OVER(1));
+ sfc_status = SFC_STATUS_OVERFLOW;
+ } else if(jz_vreadf(sr, SFC_SR, UNDER)) {
+ jz_overwritef(SFC_SCR, CLR_UNDER(1));
+ sfc_status = SFC_STATUS_UNDERFLOW;
+ } else if(jz_vreadf(sr, SFC_SR, END)) {
+ jz_overwritef(SFC_SCR, CLR_END(1));
+ sfc_status = SFC_STATUS_OK;
+ } else {
+ panicf("SFC IRQ bug");
+ return;
+ }
+
+ /* Not sure this is wholly correct */
+ if(sfc_status != SFC_STATUS_OK)
+ jz_overwritef(SFC_TRIG, STOP(1));
+
+ REG_SFC_INTC = 0x1f;
+ semaphore_release(&sfc_sema);
+}
+#else
+/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed
+ * so please do NOT try to rearrange the code without testing it first!
+ */
+
+void sfc_fifo_read(unsigned* buffer, int data_bytes)
+{
+ int data_words = (data_bytes + 3) / 4;
+ while(data_words > 0) {
+ if(jz_readf(SFC_SR, RREQ)) {
+ jz_overwritef(SFC_SCR, CLR_RREQ(1));
+
+ int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
+ data_words -= amount;
+ while(amount > 0) {
+ *buffer++ = REG_SFC_DATA;
+ amount -= 1;
+ }
+ }
+ }
+}
+
+void sfc_fifo_write(const unsigned* buffer, int data_bytes)
+{
+ int data_words = (data_bytes + 3) / 4;
+ while(data_words > 0) {
+ if(jz_readf(SFC_SR, TREQ)) {
+ jz_overwritef(SFC_SCR, CLR_TREQ(1));
+
+ int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
+ data_words -= amount;
+ while(amount > 0) {
+ REG_SFC_DATA = *buffer++;
+ amount -= 1;
+ }
+ }
+ }
+}
+
+static void sfc_wait_end(void)
+{
+ while(jz_readf(SFC_SR, END) == 0);
+ jz_overwritef(SFC_SCR, CLR_TREQ(1));
+}
+
+#endif /* NEED_SFC_DMA */
+
+int sfc_exec(const sfc_op* op)
+{
+#ifdef NEED_SFC_DMA
+ uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END);
+#endif
+
+ if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) {
+ jz_writef(SFC_TRAN_CONF(0), DATA_EN(1));
+ REG_SFC_TRAN_LENGTH = op->data_bytes;
+#ifdef NEED_SFC_DMA
+ REG_SFC_MEM_ADDR = PHYSADDR(op->buffer);
+#endif
+
+ if(op->flags & SFC_FLAG_READ)
+ {
+ jz_writef(SFC_GLB, TRAN_DIR_V(READ));
+#ifdef NEED_SFC_DMA
+ discard_dcache_range(op->buffer, op->data_bytes);
+ intc_clear |= jz_orm(SFC_INTC, MSK_OVER);
+#endif
+ }
+ else
+ {
+ jz_writef(SFC_GLB, TRAN_DIR_V(WRITE));
+#ifdef NEED_SFC_DMA
+ commit_dcache_range(op->buffer, op->data_bytes);
+ intc_clear |= jz_orm(SFC_INTC, MSK_UNDER);
+#endif
+ }
+ } else {
+ jz_writef(SFC_TRAN_CONF(0), DATA_EN(0));
+ REG_SFC_TRAN_LENGTH = 0;
+#ifdef NEED_SFC_DMA
+ REG_SFC_MEM_ADDR = 0;
+#endif
+ }
+
+ bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0;
+ jz_writef(SFC_TRAN_CONF(0),
+ MODE(op->mode), POLL_EN(0),
+ ADDR_WIDTH(op->addr_bytes),
+ PHASE_FMT(dummy_first ? 1 : 0),
+ DUMMY_BITS(op->dummy_bits),
+ COMMAND(op->command), CMD_EN(1));
+
+ REG_SFC_DEV_ADDR(0) = op->addr_lo;
+ REG_SFC_DEV_PLUS(0) = op->addr_hi;
+
+#ifdef NEED_SFC_DMA
+ sfc_status = SFC_STATUS_PENDING;
+ timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0);
+ REG_SFC_SCR = 0x1f;
+ REG_SFC_INTC &= ~intc_clear;
+#endif
+
+ jz_overwritef(SFC_TRIG, FLUSH(1));
+ jz_overwritef(SFC_TRIG, START(1));
+
+#ifndef NEED_SFC_DMA
+ if(op->flags & SFC_FLAG_READ)
+ sfc_fifo_read((unsigned*)op->buffer, op->data_bytes);
+ if(op->flags & SFC_FLAG_WRITE)
+ sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes);
+#endif
+
+ sfc_wait_end();
+
+#ifdef NEED_SFC_DMA
+ if(op->flags & SFC_FLAG_READ)
+ discard_dcache_range(op->buffer, op->data_bytes);
+#endif
+
+ return sfc_status;
+}
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
new file mode 100644
index 0000000000..283f171697
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "clk-x1000.h"
+#include "x1000/sfc.h"
+
+/* SPI flash controller interface -- this is a low-level driver upon which
+ * you can build NAND/NOR flash drivers. The main function is sfc_exec(),
+ * used to issue commands, transfer data, etc.
+ */
+
+#define SFC_FLAG_READ 0x01 /* Read data */
+#define SFC_FLAG_WRITE 0x02 /* Write data */
+#define SFC_FLAG_DUMMYFIRST 0x04 /* Do dummy bits before sending address.
+ * Default is dummy bits after address.
+ */
+
+/* SPI transfer mode. If in doubt, check with the X1000 manual and confirm
+ * the transfer format is what you expect.
+ */
+#define SFC_MODE_STANDARD 0
+#define SFC_MODE_DUAL_IN_DUAL_OUT 1
+#define SFC_MODE_DUAL_IO 2
+#define SFC_MODE_FULL_DUAL_IO 3
+#define SFC_MODE_QUAD_IN_QUAD_OUT 4
+#define SFC_MODE_QUAD_IO 5
+#define SFC_MODE_FULL_QUAD_IO 6
+
+/* Return status codes for sfc_exec() */
+#define SFC_STATUS_OK 0
+#define SFC_STATUS_OVERFLOW 1
+#define SFC_STATUS_UNDERFLOW 2
+#define SFC_STATUS_LOCKUP 3
+
+typedef struct sfc_op {
+ int command; /* Command number */
+ int mode; /* SPI transfer mode */
+ int flags; /* Flags for this op */
+ int addr_bytes; /* Number of address bytes */
+ int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */
+ uint32_t addr_lo; /* Lower 32 bits of address */
+ uint32_t addr_hi; /* Upper 32 bits of address */
+ int data_bytes; /* Number of data bytes to read/write */
+ void* buffer; /* Data buffer -- MUST be word-aligned */
+} sfc_op;
+
+/* One-time driver init for mutexes/etc needed for handling interrupts.
+ * This can be safely called multiple times; only the first call will
+ * actually perform the init.
+ */
+extern void sfc_init(void);
+
+/* Controller mutex -- lock before touching the driver */
+extern void sfc_lock(void);
+extern void sfc_unlock(void);
+
+/* Open/close the driver. The driver must be open in order to do operations.
+ * Closing the driver shuts off the hardware; the driver can be re-opened at
+ * a later time when it's needed again.
+ *
+ * After opening the driver, you must also program a valid device configuration
+ * and clock rate using sfc_set_dev_conf() and sfc_set_clock().
+ */
+extern void sfc_open(void);
+extern void sfc_close(void);
+
+/* These functions can be called at any time while the driver is open, but
+ * must not be called while there is an operation in progress. It's the
+ * caller's job to ensure the configuration will work with the device and
+ * be capable of reading back data correctly.
+ *
+ * - sfc_set_dev_conf() writes its argument to the SFC_DEV_CONF register.
+ * - sfc_set_wp_enable() sets the state of the write-protect pin (WP).
+ * - sfc_set_clock() sets the controller clock frequency (in Hz).
+ */
+#define sfc_set_dev_conf(dev_conf) \
+ do { REG_SFC_DEV_CONF = (dev_conf); } while(0)
+
+#define sfc_set_wp_enable(en) \
+ jz_writef(SFC_GLB, WP_EN((en) ? 1 : 0))
+
+extern void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq);
+
+/* Execute an operation. Returns zero on success, nonzero on failure. */
+extern int sfc_exec(const sfc_op* op);
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
new file mode 100644
index 0000000000..2a0b6b3eaa
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -0,0 +1,47 @@
+#include "config.h"
+
+OUTPUT_FORMAT("elf32-littlemips")
+OUTPUT_ARCH(MIPS)
+ENTRY(_start)
+STARTUP(target/mips/ingenic_x1000/crt0.o)
+
+/* TCSM is 16 KiB and is mapped starting at address 0xf4000000.
+ *
+ * The SPL is loaded to TCSM + 0x1000. The area below that is stack space.
+ * The first 2 KiB of SPL is just headers. The code begins at TCSM + 0x1800.
+ * The maskrom will jump to that address (via jalr) after loading the SPL.
+ */
+MEMORY { TCSM : ORIGIN = 0xf4001800, LENGTH = 0x2800 }
+
+SECTIONS
+{
+ .text :
+ {
+ *(.init.text);
+ *(.text*);
+ } > TCSM
+
+ . = ALIGN(4);
+ .rodata :
+ {
+ *(.rodata*);
+ } > TCSM
+
+ . = ALIGN(4);
+ .data :
+ {
+ *(.data*);
+ *(.sdata*);
+ } > TCSM
+
+ . = ALIGN(4);
+ .bss (NOLOAD) :
+ {
+ _bssbegin = .;
+ *(.sbss*);
+ *(.bss*);
+ *(COMMON);
+ *(.scommon*);
+ _bssend = .;
+ } > TCSM
+}
diff --git a/firmware/target/mips/ingenic_x1000/system-target.h b/firmware/target/mips/ingenic_x1000/system-target.h
new file mode 100644
index 0000000000..a2f0a6ff70
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/system-target.h
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __SYSTEM_TARGET_H__
+#define __SYSTEM_TARGET_H__
+
+/* For the sake of system.h CACHEALIGN macros.
+ * We need this to align DMA buffers, etc.
+ */
+#define CACHEALIGN_BITS 5
+#define CACHE_SIZE (16*1024)
+
+#include "mmu-mips.h"
+#include "mipsregs.h"
+#include "mipsr2-endian.h"
+#include <stdint.h>
+
+/* Get physical address for DMA */
+#define PHYSADDR(addr) (((unsigned long)(addr)) & 0x1fffffff)
+
+#define HIGHEST_IRQ_LEVEL 0
+
+/* Rockbox API */
+#define enable_irq() set_c0_status(ST0_IE)
+#define disable_irq() clear_c0_status(ST0_IE)
+#define disable_irq_save() set_irq_level(0)
+#define restore_irq(arg) write_c0_status(arg)
+
+static inline int set_irq_level(int lev)
+{
+ unsigned reg, oldreg;
+ reg = oldreg = read_c0_status();
+ if(lev)
+ reg |= ST0_IE;
+ else
+ reg &= ~ST0_IE;
+
+ write_c0_status(reg);
+ return oldreg;
+}
+
+/* CPU idle stats, updated each kernel tick in kernel-x1000.c */
+extern int __cpu_idle_avg;
+extern int __cpu_idle_cur;
+extern uint32_t __cpu_idle_ticks;
+extern uint32_t __cpu_idle_reftick;
+
+static inline uint32_t __ost_read32(void);
+static inline void core_sleep(void)
+{
+ uint32_t t1 = __ost_read32();
+
+ __asm__ __volatile__(
+ ".set push\n\t"
+ ".set mips32r2\n\t"
+ "mfc0 $8, $12\n\t"
+ "move $9, $8\n\t"
+ "la $10, 0x8000000\n\t"
+ "or $8, $10\n\t"
+ "mtc0 $8, $12\n\t"
+ "wait\n\t"
+ "mtc0 $9, $12\n\t"
+ ".set pop\n\t"
+ ::: "t0", "t1", "t2");
+
+ uint32_t t2 = __ost_read32();
+ __cpu_idle_ticks += t2 - t1;
+
+ enable_irq();
+}
+
+/* IRQ control */
+extern void system_enable_irq(int irq);
+extern void system_disable_irq(int irq);
+
+/* Simple delay API */
+#define OST_FREQUENCY (X1000_EXCLK_FREQ / 4)
+#define OST_TICKS_PER_US (OST_FREQUENCY / 1000000)
+#define MAX_OST_DELAY_ARG 0x7fffffff
+#define MAX_UDELAY_ARG (MAX_OST_DELAY_ARG / OST_TICKS_PER_US)
+#define MAX_MDELAY_ARG (MAX_UDELAY_ARG / 1000)
+
+/* Macros adapted from include/linux/delay.h,
+ * Copyright (C) 1993 Linus Torvalds
+ *
+ * These optimize away all calculations to compile time for the common case
+ * of small constant arguments, reducing to a single __ost_delay() call.
+ */
+
+#define udelay(n) \
+ ((__builtin_constant_p(n) && (n) <= MAX_UDELAY_ARG) ? \
+ __ost_delay((n) * OST_TICKS_PER_US) : __udelay((n)))
+
+#define mdelay(n) \
+ ((__builtin_constant_p(n) && (n) <= MAX_MDELAY_ARG) ? \
+ __ost_delay((n) * 1000 * OST_TICKS_PER_US) : __mdelay((n)))
+
+/* Slow path implementations which handle their full argument range by
+ * looping and calling __ost_delay() repeatedly.
+ */
+extern void __udelay(uint32_t us);
+extern void __mdelay(uint32_t ms);
+
+/* Read full 64-bit OST counter value; this requires disabling IRQs
+ * to safely read the counter.
+ */
+extern uint64_t __ost_read64(void);
+
+static inline uint32_t __ost_read32(void)
+{
+ /* Read OST_2CNTL using raw address to avoid exposing internal headers.
+ * The 64-bit counter is read with IRQs disabled and since threads are
+ * not pre-emptive in Rockbox we won't trash anybody's 64-bit read by
+ * reading the low count without locking.
+ */
+ return *(const volatile uint32_t*)0xb2000020;
+}
+
+/* NOTE: it is required that count < MAX_OST_DELAY_ARG, this is to provide
+ * some slack in the 32-bit counter so we can reliably detect the timeout.
+ */
+static inline void __ost_delay(uint32_t count)
+{
+ /* Add one to ensure we delay for at least the time given */
+ count += 1;
+ uint32_t start = __ost_read32();
+ while(__ost_read32() - start < count);
+}
+
+#endif /* __SYSTEM_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/system-x1000.c b/firmware/target/mips/ingenic_x1000/system-x1000.c
new file mode 100644
index 0000000000..54513cffb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/system-x1000.c
@@ -0,0 +1,418 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "mips.h"
+#include "panic.h"
+#include "button.h"
+#include "gpio-x1000.h"
+#include "dma-x1000.h"
+#include "irq-x1000.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/ost.h"
+#include "x1000/tcu.h"
+#include "x1000/wdt.h"
+#include "x1000/intc.h"
+#include "x1000/msc.h"
+#include "x1000/aic.h"
+
+int __cpu_idle_avg = 0;
+int __cpu_idle_cur = 0;
+uint32_t __cpu_idle_ticks = 0;
+uint32_t __cpu_idle_reftick = 0;
+
+static void system_init_clk(void)
+{
+ /* Gate all clocks except CPU/bus/memory/RTC */
+ REG_CPM_CLKGR = ~jz_orm(CPM_CLKGR, CPU_BIT, DDR, AHB0, APB0, RTC);
+
+ /* Switch to EXCLK */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
+ clk_set_ccr_div(1, 1, 1, 1, 1);
+
+#ifdef FIIO_M3K
+ /* Nominal clock configuration
+ * ---------------------------
+ * APLL at 1 GHz, MPLL disabled
+ * CPU at 1 GHz, L2 cache at 500 MHz
+ * AHB0 and AHB2 at 200 MHz
+ * PCLK at 100 MHz
+ * DDR at 200 MHz
+ */
+ jz_writef(CPM_APCR, BS(1), PLLM(41), PLLN(0), PLLOD(0), ENABLE(1));
+ while(jz_readf(CPM_APCR, ON) == 0);
+
+ clk_set_ccr_div(1, 2, 5, 5, 10);
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
+ clk_set_ddr(X1000_CLK_SCLK_A, 5);
+
+ /* Shut off MPLL, since nobody should be using it now */
+ jz_writef(CPM_MPCR, ENABLE(0));
+#else
+# error "Please define system clock configuration for target"
+#endif
+}
+
+/* Prepare the CPU to process interrupts, but don't enable them yet */
+static void system_init_irq(void)
+{
+ /* Mask all interrupts */
+ jz_set(INTC_MSK(0), 0xffffffff);
+ jz_set(INTC_MSK(1), 0xffffffff);
+
+ /* It's safe to unmask these unconditionally */
+ jz_clr(INTC_MSK(0), (1 << IRQ0_GPIO0) | (1 << IRQ0_GPIO1) |
+ (1 << IRQ0_GPIO2) | (1 << IRQ0_GPIO3) |
+ (1 << IRQ0_TCU1));
+
+ /* Setup CP0 registers */
+ write_c0_status(M_StatusCU0 | M_StatusIM2 | M_StatusIM3);
+ write_c0_cause(M_CauseIV);
+}
+
+/* First thing called from Rockbox main() */
+void system_init(void)
+{
+ /* Setup system clocks */
+ system_init_clk();
+
+ /* Ungate timers and turn them all off by default */
+ jz_writef(CPM_CLKGR, TCU(0), OST(0));
+ jz_clrf(OST_ENABLE, OST1, OST2);
+ jz_write(OST_1MSK, 1);
+ jz_write(OST_1FLG, 0);
+ jz_clr(TCU_ENABLE, 0x80ff);
+ jz_set(TCU_MASK, 0xff10ff);
+ jz_clr(TCU_FLAG, 0xff10ff);
+ jz_set(TCU_STOP, 0x180ff);
+
+ /* Start OST2, needed for delay timer */
+ jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
+ jz_writef(OST_CLEAR, OST2(1));
+ jz_write(OST_2CNTH, 0);
+ jz_write(OST_2CNTL, 0);
+ jz_setf(OST_ENABLE, OST2);
+
+ /* Ensure CPU sleep mode is IDLE and not SLEEP */
+ jz_writef(CPM_LCR, LPM_V(IDLE));
+
+ /* All other init */
+ gpio_init();
+ system_init_irq();
+ dma_init();
+ mmu_init();
+}
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+void set_cpu_frequency(long tgt_freq)
+{
+ /* Clamp target frequency to "sane" values */
+ if(tgt_freq < 0) tgt_freq = 0;
+ if(tgt_freq > CPU_FREQ) tgt_freq = CPU_FREQ;
+
+ /* Find out input clock */
+ uint32_t in_freq;
+ switch(jz_readf(CPM_CCR, SEL_CPLL)) {
+ case 1: in_freq = clk_get(X1000_CLK_SCLK_A); break;
+ case 2: in_freq = clk_get(X1000_CLK_MPLL); break;
+ default: return;
+ }
+
+ /* Clamp to valid range */
+ if(tgt_freq < 1)
+ tgt_freq = 1;
+ if(tgt_freq > (long)in_freq)
+ tgt_freq = in_freq;
+
+ /* Calculate CPU clock divider */
+ uint32_t cdiv = clk_calc_div(in_freq, tgt_freq);
+ if(cdiv > 16) cdiv = 16;
+ if(cdiv < 1) cdiv = 1;
+
+ /* Calculate L2 cache clock. */
+ uint32_t l2div = cdiv;
+ if(cdiv == 1)
+ l2div = 2;
+
+ /* Change CPU/L2 frequency */
+ jz_writef(CPM_CCR, CE_CPU(1), L2DIV(l2div - 1), CDIV(cdiv - 1));
+ while(jz_readf(CPM_CSR, CDIV_BUSY));
+ jz_writef(CPM_CCR, CE_CPU(0));
+
+ /* Update value for Rockbox */
+ cpu_frequency = in_freq / cdiv;
+}
+#endif
+
+void system_reboot(void)
+{
+ jz_clr(TCU_STOP, 0x10000);
+ jz_writef(WDT_CTRL, PRESCALE_V(BY_4), SOURCE_V(EXT));
+ jz_write(WDT_COUNT, 0);
+ jz_write(WDT_DATA, X1000_EXCLK_FREQ / 1000);
+ jz_write(WDT_ENABLE, 1);
+ while(1);
+}
+
+int system_memory_guard(int mode)
+{
+ /* unused */
+ (void)mode;
+ return 0;
+}
+
+/* Simple delay API -- slow path functions */
+
+void __udelay(uint32_t us)
+{
+ while(us > MAX_UDELAY_ARG) {
+ __ost_delay(MAX_UDELAY_ARG * OST_TICKS_PER_US);
+ us -= MAX_UDELAY_ARG;
+ }
+
+ __ost_delay(us * OST_TICKS_PER_US);
+}
+
+void __mdelay(uint32_t ms)
+{
+ while(ms > MAX_MDELAY_ARG) {
+ __ost_delay(MAX_MDELAY_ARG * 1000 * OST_TICKS_PER_US);
+ ms -= MAX_MDELAY_ARG;
+ }
+
+ __ost_delay(ms * 1000 * OST_TICKS_PER_US);
+}
+
+uint64_t __ost_read64(void)
+{
+ int irq = disable_irq_save();
+ uint64_t lcnt = REG_OST_2CNTL;
+ uint64_t hcnt = REG_OST_2CNTHB;
+ restore_irq(irq);
+ return (hcnt << 32) | lcnt;
+}
+
+/* IRQ handling */
+static int irq = 0;
+static unsigned ipr0 = 0, ipr1 = 0;
+
+static void UIRQ(void)
+{
+ panicf("Unhandled interrupt occurred: %d", irq);
+}
+
+#define intr(name) extern __attribute__((weak, alias("UIRQ"))) void name(void)
+
+/* Main interrupts */
+intr(DMIC); intr(AIC); intr(SFC); intr(SSI0); intr(OTG); intr(AES);
+intr(TCU2); intr(TCU1); intr(TCU0); intr(CIM); intr(LCD); intr(RTC);
+intr(MSC1); intr(MSC0); intr(SCC); intr(PCM0); intr(HARB2); intr(HARB0);
+intr(CPM); intr(UART2); intr(UART1); intr(UART0); intr(DDR); intr(EFUSE);
+intr(MAC); intr(I2C2); intr(I2C1); intr(I2C0); intr(JPEG);
+intr(PDMA); intr(PDMAD); intr(PDMAM);
+/* GPIO A - 32 pins */
+intr(GPIOA00); intr(GPIOA01); intr(GPIOA02); intr(GPIOA03); intr(GPIOA04);
+intr(GPIOA05); intr(GPIOA06); intr(GPIOA07); intr(GPIOA08); intr(GPIOA09);
+intr(GPIOA10); intr(GPIOA11); intr(GPIOA12); intr(GPIOA13); intr(GPIOA14);
+intr(GPIOA15); intr(GPIOA16); intr(GPIOA17); intr(GPIOA18); intr(GPIOA19);
+intr(GPIOA20); intr(GPIOA21); intr(GPIOA22); intr(GPIOA23); intr(GPIOA24);
+intr(GPIOA25); intr(GPIOA26); intr(GPIOA27); intr(GPIOA28); intr(GPIOA29);
+intr(GPIOA30); intr(GPIOA31);
+/* GPIO B - 32 pins */
+intr(GPIOB00); intr(GPIOB01); intr(GPIOB02); intr(GPIOB03); intr(GPIOB04);
+intr(GPIOB05); intr(GPIOB06); intr(GPIOB07); intr(GPIOB08); intr(GPIOB09);
+intr(GPIOB10); intr(GPIOB11); intr(GPIOB12); intr(GPIOB13); intr(GPIOB14);
+intr(GPIOB15); intr(GPIOB16); intr(GPIOB17); intr(GPIOB18); intr(GPIOB19);
+intr(GPIOB20); intr(GPIOB21); intr(GPIOB22); intr(GPIOB23); intr(GPIOB24);
+intr(GPIOB25); intr(GPIOB26); intr(GPIOB27); intr(GPIOB28); intr(GPIOB29);
+intr(GPIOB30); intr(GPIOB31);
+/* GPIO C - 26 pins */
+intr(GPIOC00); intr(GPIOC01); intr(GPIOC02); intr(GPIOC03); intr(GPIOC04);
+intr(GPIOC05); intr(GPIOC06); intr(GPIOC07); intr(GPIOC08); intr(GPIOC09);
+intr(GPIOC10); intr(GPIOC11); intr(GPIOC12); intr(GPIOC13); intr(GPIOC14);
+intr(GPIOC15); intr(GPIOC16); intr(GPIOC17); intr(GPIOC18); intr(GPIOC19);
+intr(GPIOC20); intr(GPIOC21); intr(GPIOC22); intr(GPIOC23); intr(GPIOC24);
+intr(GPIOC25);
+/* GPIO D - 6 pins */
+intr(GPIOD00); intr(GPIOD01); intr(GPIOD02); intr(GPIOD03); intr(GPIOD04);
+intr(GPIOD05);
+
+/* OST interrupt -- has no IRQ number since it's got special handling */
+intr(OST);
+
+#undef intr
+
+static void(*const irqvector[])(void) = {
+ /* ICSR0: 0 - 31 */
+ DMIC, AIC, UIRQ, UIRQ, UIRQ, UIRQ, UIRQ, SFC,
+ SSI0, UIRQ, PDMA, PDMAD, UIRQ, UIRQ, UIRQ, UIRQ,
+ UIRQ, UIRQ, UIRQ, UIRQ, UIRQ, OTG, UIRQ, AES,
+ UIRQ, TCU2, TCU1, TCU0, UIRQ, UIRQ, CIM, LCD,
+ /* ICSR1: 32 - 63 */
+ RTC, UIRQ, UIRQ, UIRQ, MSC1, MSC0, SCC, UIRQ,
+ PCM0, UIRQ, UIRQ, UIRQ, HARB2, UIRQ, HARB0, CPM,
+ UIRQ, UART2, UART1, UART0, DDR, UIRQ, EFUSE, MAC,
+ UIRQ, UIRQ, I2C2, I2C1, I2C0, PDMAM, JPEG, UIRQ,
+ /* GPIO A: 64 - 95 */
+ GPIOA00, GPIOA01, GPIOA02, GPIOA03, GPIOA04, GPIOA05, GPIOA06, GPIOA07,
+ GPIOA08, GPIOA09, GPIOA10, GPIOA11, GPIOA12, GPIOA13, GPIOA14, GPIOA15,
+ GPIOA16, GPIOA17, GPIOA18, GPIOA19, GPIOA20, GPIOA21, GPIOA22, GPIOA23,
+ GPIOA24, GPIOA25, GPIOA26, GPIOA27, GPIOA28, GPIOA29, GPIOA30, GPIOA31,
+ /* GPIO B: 96 - 127 */
+ GPIOB00, GPIOB01, GPIOB02, GPIOB03, GPIOB04, GPIOB05, GPIOB06, GPIOB07,
+ GPIOB08, GPIOB09, GPIOB10, GPIOB11, GPIOB12, GPIOB13, GPIOB14, GPIOB15,
+ GPIOB16, GPIOB17, GPIOB18, GPIOB19, GPIOB20, GPIOB21, GPIOB22, GPIOB23,
+ GPIOB24, GPIOB25, GPIOB26, GPIOB27, GPIOB28, GPIOB29, GPIOB30, GPIOB31,
+ /* GPIO C: 128 - 159 */
+ GPIOC00, GPIOC01, GPIOC02, GPIOC03, GPIOC04, GPIOC05, GPIOC06, GPIOC07,
+ GPIOC08, GPIOC09, GPIOC10, GPIOC11, GPIOC12, GPIOC13, GPIOC14, GPIOC15,
+ GPIOC16, GPIOC17, GPIOC18, GPIOC19, GPIOC20, GPIOC21, GPIOC22, GPIOC23,
+ GPIOC24, GPIOC25, UIRQ, UIRQ, UIRQ, UIRQ, UIRQ, UIRQ,
+ /* GPIO D: 160 - 165 */
+ GPIOD00, GPIOD01, GPIOD02, GPIOD03, GPIOD04, GPIOD05,
+};
+
+void system_enable_irq(int irq)
+{
+ if(IRQ_IS_GROUP0(irq)) {
+ jz_clr(INTC_MSK(0), 1 << IRQ_TO_GROUP0(irq));
+ } else if(IRQ_IS_GROUP1(irq)) {
+ jz_clr(INTC_MSK(1), 1 << IRQ_TO_GROUP1(irq));
+ }
+}
+
+void system_disable_irq(int irq)
+{
+ if(IRQ_IS_GROUP0(irq)) {
+ jz_set(INTC_MSK(0), 1 << IRQ_TO_GROUP0(irq));
+ } else if(IRQ_IS_GROUP1(irq)) {
+ jz_set(INTC_MSK(1), 1 << IRQ_TO_GROUP1(irq));
+ }
+}
+
+static int vector_gpio_irq(int port)
+{
+ int n = find_first_set_bit(REG_GPIO_FLAG(port));
+ if(n & 32)
+ return -1;
+
+ jz_clr(GPIO_FLAG(port), 1 << n);
+ return IRQ_GPIO(port, n);
+}
+
+static int vector_irq(void)
+{
+ int n = find_first_set_bit(ipr0);
+ if(n & 32) {
+ n = find_first_set_bit(ipr1);
+ if(n & 32)
+ return -1;
+ ipr1 &= ~(1 << n);
+ n += 32;
+ } else {
+ ipr0 &= ~(1 << n);
+ }
+
+ switch(n) {
+ case IRQ0_GPIO0: n = vector_gpio_irq(GPIO_A); break;
+ case IRQ0_GPIO1: n = vector_gpio_irq(GPIO_B); break;
+ case IRQ0_GPIO2: n = vector_gpio_irq(GPIO_C); break;
+ case IRQ0_GPIO3: n = vector_gpio_irq(GPIO_D); break;
+ default: break;
+ }
+
+ return n;
+}
+
+void intr_handler(unsigned cause)
+{
+ /* OST interrupt is handled separately */
+ if(cause & M_CauseIP3) {
+ OST();
+ return;
+ }
+
+ /* Gather pending interrupts */
+ ipr0 |= REG_INTC_PND(0);
+ ipr1 |= REG_INTC_PND(1);
+
+ /* Process and dispatch interrupt */
+ irq = vector_irq();
+ if(irq < 0)
+ return;
+
+ irqvector[irq]();
+}
+
+void tlb_refill_handler(void)
+{
+ panicf("TLB refill handler at 0x%08lx! [0x%x]",
+ read_c0_epc(), read_c0_badvaddr());
+}
+
+#define EXC(x,y) case (x): return (y);
+static char* parse_exception(unsigned cause)
+{
+ switch(cause & M_CauseExcCode)
+ {
+ EXC(EXC_INT, "Interrupt");
+ EXC(EXC_MOD, "TLB Modified");
+ EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
+ EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
+ EXC(EXC_ADES, "Address Error (Store)");
+ EXC(EXC_TLBS, "TLB Exception (Store)");
+ EXC(EXC_IBE, "Instruction Bus Error");
+ EXC(EXC_DBE, "Data Bus Error");
+ EXC(EXC_SYS, "Syscall");
+ EXC(EXC_BP, "Breakpoint");
+ EXC(EXC_RI, "Reserved Instruction");
+ EXC(EXC_CPU, "Coprocessor Unusable");
+ EXC(EXC_OV, "Overflow");
+ EXC(EXC_TR, "Trap Instruction");
+ EXC(EXC_FPE, "Floating Point Exception");
+ EXC(EXC_C2E, "COP2 Exception");
+ EXC(EXC_MDMX, "MDMX Exception");
+ EXC(EXC_WATCH, "Watch Exception");
+ EXC(EXC_MCHECK, "Machine Check Exception");
+ EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
+ default:
+ return 0;
+ }
+}
+#undef EXC
+
+void exception_handler(unsigned cause, unsigned epc, unsigned stack_ptr)
+{
+ panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)",
+ parse_exception(cause), read_c0_badvaddr(), epc, stack_ptr);
+}
+
+void system_exception_wait(void)
+{
+#ifdef FIIO_M3K
+ while(button_read_device() != (BUTTON_POWER|BUTTON_VOL_DOWN));
+#else
+ while(1);
+#endif
+}
diff --git a/firmware/target/mips/ingenic_x1000/timer-x1000.c b/firmware/target/mips/ingenic_x1000/timer-x1000.c
new file mode 100644
index 0000000000..de97cbb3a3
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/timer-x1000.c
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "timer.h"
+#include "x1000/tcu.h"
+
+#define TIMER_CHN 5
+
+bool timer_set(long cycles, bool start)
+{
+ if(cycles <= 0)
+ return false;
+
+ /* Calculate timer interval */
+ unsigned long counter = cycles;
+ unsigned prescale = 0;
+ while(counter > 0xffff && prescale < 5) {
+ counter /= 4;
+ prescale += 1;
+ }
+
+ /* Duration too long */
+ if(counter > 0xffff)
+ return false;
+
+ /* Unregister old function */
+ if(start && pfn_unregister) {
+ pfn_unregister();
+ pfn_unregister = 0;
+ }
+
+ /* Configure the timer */
+ jz_clr(TCU_STOP, 1 << TIMER_CHN);
+ jz_clr(TCU_ENABLE, 1 << TIMER_CHN);
+ jz_overwritef(TCU_CTRL(TIMER_CHN), SOURCE_V(EXT), PRESCALE(prescale));
+ jz_write(TCU_CMP_FULL(TIMER_CHN), counter);
+ jz_write(TCU_CMP_HALF(TIMER_CHN), 0);
+ jz_clr(TCU_FLAG, 1 << TIMER_CHN);
+ jz_clr(TCU_MASK, 1 << TIMER_CHN);
+
+ if(start)
+ return timer_start();
+ else
+ return true;
+}
+
+bool timer_start(void)
+{
+ jz_set(TCU_ENABLE, 1 << TIMER_CHN);
+ return true;
+}
+
+void timer_stop(void)
+{
+ jz_clr(TCU_ENABLE, 1 << TIMER_CHN);
+ jz_set(TCU_MASK, 1 << TIMER_CHN);
+ jz_clr(TCU_FLAG, 1 << TIMER_CHN);
+ jz_set(TCU_STOP, 1 << TIMER_CHN);
+}
+
+void TCU1(void)
+{
+ jz_clr(TCU_FLAG, 1 << TIMER_CHN);
+
+ if(pfn_timer)
+ pfn_timer();
+}
diff --git a/firmware/target/mips/ingenic_x1000/x1000/aic.h b/firmware/target/mips/ingenic_x1000/x1000/aic.h
new file mode 100644
index 0000000000..e9c68511d7
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/aic.h
@@ -0,0 +1,359 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_AIC_H__
+#define __HEADERGEN_AIC_H__
+
+#include "macro.h"
+
+#define REG_AIC_CFG jz_reg(AIC_CFG)
+#define JA_AIC_CFG (0xb0020000 + 0x0)
+#define JT_AIC_CFG JIO_32_RW
+#define JN_AIC_CFG AIC_CFG
+#define JI_AIC_CFG
+#define BP_AIC_CFG_RFTH 24
+#define BM_AIC_CFG_RFTH 0xf000000
+#define BF_AIC_CFG_RFTH(v) (((v) & 0xf) << 24)
+#define BFM_AIC_CFG_RFTH(v) BM_AIC_CFG_RFTH
+#define BF_AIC_CFG_RFTH_V(e) BF_AIC_CFG_RFTH(BV_AIC_CFG_RFTH__##e)
+#define BFM_AIC_CFG_RFTH_V(v) BM_AIC_CFG_RFTH
+#define BP_AIC_CFG_TFTH 16
+#define BM_AIC_CFG_TFTH 0x1f0000
+#define BF_AIC_CFG_TFTH(v) (((v) & 0x1f) << 16)
+#define BFM_AIC_CFG_TFTH(v) BM_AIC_CFG_TFTH
+#define BF_AIC_CFG_TFTH_V(e) BF_AIC_CFG_TFTH(BV_AIC_CFG_TFTH__##e)
+#define BFM_AIC_CFG_TFTH_V(v) BM_AIC_CFG_TFTH
+#define BP_AIC_CFG_MSB 12
+#define BM_AIC_CFG_MSB 0x1000
+#define BF_AIC_CFG_MSB(v) (((v) & 0x1) << 12)
+#define BFM_AIC_CFG_MSB(v) BM_AIC_CFG_MSB
+#define BF_AIC_CFG_MSB_V(e) BF_AIC_CFG_MSB(BV_AIC_CFG_MSB__##e)
+#define BFM_AIC_CFG_MSB_V(v) BM_AIC_CFG_MSB
+#define BP_AIC_CFG_IBCKD 10
+#define BM_AIC_CFG_IBCKD 0x400
+#define BF_AIC_CFG_IBCKD(v) (((v) & 0x1) << 10)
+#define BFM_AIC_CFG_IBCKD(v) BM_AIC_CFG_IBCKD
+#define BF_AIC_CFG_IBCKD_V(e) BF_AIC_CFG_IBCKD(BV_AIC_CFG_IBCKD__##e)
+#define BFM_AIC_CFG_IBCKD_V(v) BM_AIC_CFG_IBCKD
+#define BP_AIC_CFG_ISYNCD 9
+#define BM_AIC_CFG_ISYNCD 0x200
+#define BF_AIC_CFG_ISYNCD(v) (((v) & 0x1) << 9)
+#define BFM_AIC_CFG_ISYNCD(v) BM_AIC_CFG_ISYNCD
+#define BF_AIC_CFG_ISYNCD_V(e) BF_AIC_CFG_ISYNCD(BV_AIC_CFG_ISYNCD__##e)
+#define BFM_AIC_CFG_ISYNCD_V(v) BM_AIC_CFG_ISYNCD
+#define BP_AIC_CFG_DMODE 8
+#define BM_AIC_CFG_DMODE 0x100
+#define BF_AIC_CFG_DMODE(v) (((v) & 0x1) << 8)
+#define BFM_AIC_CFG_DMODE(v) BM_AIC_CFG_DMODE
+#define BF_AIC_CFG_DMODE_V(e) BF_AIC_CFG_DMODE(BV_AIC_CFG_DMODE__##e)
+#define BFM_AIC_CFG_DMODE_V(v) BM_AIC_CFG_DMODE
+#define BP_AIC_CFG_CDC_SLAVE 7
+#define BM_AIC_CFG_CDC_SLAVE 0x80
+#define BF_AIC_CFG_CDC_SLAVE(v) (((v) & 0x1) << 7)
+#define BFM_AIC_CFG_CDC_SLAVE(v) BM_AIC_CFG_CDC_SLAVE
+#define BF_AIC_CFG_CDC_SLAVE_V(e) BF_AIC_CFG_CDC_SLAVE(BV_AIC_CFG_CDC_SLAVE__##e)
+#define BFM_AIC_CFG_CDC_SLAVE_V(v) BM_AIC_CFG_CDC_SLAVE
+#define BP_AIC_CFG_LSMP 6
+#define BM_AIC_CFG_LSMP 0x40
+#define BF_AIC_CFG_LSMP(v) (((v) & 0x1) << 6)
+#define BFM_AIC_CFG_LSMP(v) BM_AIC_CFG_LSMP
+#define BF_AIC_CFG_LSMP_V(e) BF_AIC_CFG_LSMP(BV_AIC_CFG_LSMP__##e)
+#define BFM_AIC_CFG_LSMP_V(v) BM_AIC_CFG_LSMP
+#define BP_AIC_CFG_ICDC 5
+#define BM_AIC_CFG_ICDC 0x20
+#define BF_AIC_CFG_ICDC(v) (((v) & 0x1) << 5)
+#define BFM_AIC_CFG_ICDC(v) BM_AIC_CFG_ICDC
+#define BF_AIC_CFG_ICDC_V(e) BF_AIC_CFG_ICDC(BV_AIC_CFG_ICDC__##e)
+#define BFM_AIC_CFG_ICDC_V(v) BM_AIC_CFG_ICDC
+#define BP_AIC_CFG_AUSEL 4
+#define BM_AIC_CFG_AUSEL 0x10
+#define BF_AIC_CFG_AUSEL(v) (((v) & 0x1) << 4)
+#define BFM_AIC_CFG_AUSEL(v) BM_AIC_CFG_AUSEL
+#define BF_AIC_CFG_AUSEL_V(e) BF_AIC_CFG_AUSEL(BV_AIC_CFG_AUSEL__##e)
+#define BFM_AIC_CFG_AUSEL_V(v) BM_AIC_CFG_AUSEL
+#define BP_AIC_CFG_RST 3
+#define BM_AIC_CFG_RST 0x8
+#define BF_AIC_CFG_RST(v) (((v) & 0x1) << 3)
+#define BFM_AIC_CFG_RST(v) BM_AIC_CFG_RST
+#define BF_AIC_CFG_RST_V(e) BF_AIC_CFG_RST(BV_AIC_CFG_RST__##e)
+#define BFM_AIC_CFG_RST_V(v) BM_AIC_CFG_RST
+#define BP_AIC_CFG_BCKD 2
+#define BM_AIC_CFG_BCKD 0x4
+#define BF_AIC_CFG_BCKD(v) (((v) & 0x1) << 2)
+#define BFM_AIC_CFG_BCKD(v) BM_AIC_CFG_BCKD
+#define BF_AIC_CFG_BCKD_V(e) BF_AIC_CFG_BCKD(BV_AIC_CFG_BCKD__##e)
+#define BFM_AIC_CFG_BCKD_V(v) BM_AIC_CFG_BCKD
+#define BP_AIC_CFG_SYNCD 1
+#define BM_AIC_CFG_SYNCD 0x2
+#define BF_AIC_CFG_SYNCD(v) (((v) & 0x1) << 1)
+#define BFM_AIC_CFG_SYNCD(v) BM_AIC_CFG_SYNCD
+#define BF_AIC_CFG_SYNCD_V(e) BF_AIC_CFG_SYNCD(BV_AIC_CFG_SYNCD__##e)
+#define BFM_AIC_CFG_SYNCD_V(v) BM_AIC_CFG_SYNCD
+#define BP_AIC_CFG_ENABLE 0
+#define BM_AIC_CFG_ENABLE 0x1
+#define BF_AIC_CFG_ENABLE(v) (((v) & 0x1) << 0)
+#define BFM_AIC_CFG_ENABLE(v) BM_AIC_CFG_ENABLE
+#define BF_AIC_CFG_ENABLE_V(e) BF_AIC_CFG_ENABLE(BV_AIC_CFG_ENABLE__##e)
+#define BFM_AIC_CFG_ENABLE_V(v) BM_AIC_CFG_ENABLE
+
+#define REG_AIC_CCR jz_reg(AIC_CCR)
+#define JA_AIC_CCR (0xb0020000 + 0x4)
+#define JT_AIC_CCR JIO_32_RW
+#define JN_AIC_CCR AIC_CCR
+#define JI_AIC_CCR
+#define BP_AIC_CCR_CHANNEL 24
+#define BM_AIC_CCR_CHANNEL 0x7000000
+#define BF_AIC_CCR_CHANNEL(v) (((v) & 0x7) << 24)
+#define BFM_AIC_CCR_CHANNEL(v) BM_AIC_CCR_CHANNEL
+#define BF_AIC_CCR_CHANNEL_V(e) BF_AIC_CCR_CHANNEL(BV_AIC_CCR_CHANNEL__##e)
+#define BFM_AIC_CCR_CHANNEL_V(v) BM_AIC_CCR_CHANNEL
+#define BP_AIC_CCR_OSS 19
+#define BM_AIC_CCR_OSS 0x380000
+#define BF_AIC_CCR_OSS(v) (((v) & 0x7) << 19)
+#define BFM_AIC_CCR_OSS(v) BM_AIC_CCR_OSS
+#define BF_AIC_CCR_OSS_V(e) BF_AIC_CCR_OSS(BV_AIC_CCR_OSS__##e)
+#define BFM_AIC_CCR_OSS_V(v) BM_AIC_CCR_OSS
+#define BP_AIC_CCR_ISS 16
+#define BM_AIC_CCR_ISS 0x70000
+#define BF_AIC_CCR_ISS(v) (((v) & 0x7) << 16)
+#define BFM_AIC_CCR_ISS(v) BM_AIC_CCR_ISS
+#define BF_AIC_CCR_ISS_V(e) BF_AIC_CCR_ISS(BV_AIC_CCR_ISS__##e)
+#define BFM_AIC_CCR_ISS_V(v) BM_AIC_CCR_ISS
+#define BP_AIC_CCR_PACK16 28
+#define BM_AIC_CCR_PACK16 0x10000000
+#define BF_AIC_CCR_PACK16(v) (((v) & 0x1) << 28)
+#define BFM_AIC_CCR_PACK16(v) BM_AIC_CCR_PACK16
+#define BF_AIC_CCR_PACK16_V(e) BF_AIC_CCR_PACK16(BV_AIC_CCR_PACK16__##e)
+#define BFM_AIC_CCR_PACK16_V(v) BM_AIC_CCR_PACK16
+#define BP_AIC_CCR_RDMS 15
+#define BM_AIC_CCR_RDMS 0x8000
+#define BF_AIC_CCR_RDMS(v) (((v) & 0x1) << 15)
+#define BFM_AIC_CCR_RDMS(v) BM_AIC_CCR_RDMS
+#define BF_AIC_CCR_RDMS_V(e) BF_AIC_CCR_RDMS(BV_AIC_CCR_RDMS__##e)
+#define BFM_AIC_CCR_RDMS_V(v) BM_AIC_CCR_RDMS
+#define BP_AIC_CCR_TDMS 14
+#define BM_AIC_CCR_TDMS 0x4000
+#define BF_AIC_CCR_TDMS(v) (((v) & 0x1) << 14)
+#define BFM_AIC_CCR_TDMS(v) BM_AIC_CCR_TDMS
+#define BF_AIC_CCR_TDMS_V(e) BF_AIC_CCR_TDMS(BV_AIC_CCR_TDMS__##e)
+#define BFM_AIC_CCR_TDMS_V(v) BM_AIC_CCR_TDMS
+#define BP_AIC_CCR_M2S 11
+#define BM_AIC_CCR_M2S 0x800
+#define BF_AIC_CCR_M2S(v) (((v) & 0x1) << 11)
+#define BFM_AIC_CCR_M2S(v) BM_AIC_CCR_M2S
+#define BF_AIC_CCR_M2S_V(e) BF_AIC_CCR_M2S(BV_AIC_CCR_M2S__##e)
+#define BFM_AIC_CCR_M2S_V(v) BM_AIC_CCR_M2S
+#define BP_AIC_CCR_ENDSW 10
+#define BM_AIC_CCR_ENDSW 0x400
+#define BF_AIC_CCR_ENDSW(v) (((v) & 0x1) << 10)
+#define BFM_AIC_CCR_ENDSW(v) BM_AIC_CCR_ENDSW
+#define BF_AIC_CCR_ENDSW_V(e) BF_AIC_CCR_ENDSW(BV_AIC_CCR_ENDSW__##e)
+#define BFM_AIC_CCR_ENDSW_V(v) BM_AIC_CCR_ENDSW
+#define BP_AIC_CCR_ASVTSU 9
+#define BM_AIC_CCR_ASVTSU 0x200
+#define BF_AIC_CCR_ASVTSU(v) (((v) & 0x1) << 9)
+#define BFM_AIC_CCR_ASVTSU(v) BM_AIC_CCR_ASVTSU
+#define BF_AIC_CCR_ASVTSU_V(e) BF_AIC_CCR_ASVTSU(BV_AIC_CCR_ASVTSU__##e)
+#define BFM_AIC_CCR_ASVTSU_V(v) BM_AIC_CCR_ASVTSU
+#define BP_AIC_CCR_TFLUSH 8
+#define BM_AIC_CCR_TFLUSH 0x100
+#define BF_AIC_CCR_TFLUSH(v) (((v) & 0x1) << 8)
+#define BFM_AIC_CCR_TFLUSH(v) BM_AIC_CCR_TFLUSH
+#define BF_AIC_CCR_TFLUSH_V(e) BF_AIC_CCR_TFLUSH(BV_AIC_CCR_TFLUSH__##e)
+#define BFM_AIC_CCR_TFLUSH_V(v) BM_AIC_CCR_TFLUSH
+#define BP_AIC_CCR_RFLUSH 7
+#define BM_AIC_CCR_RFLUSH 0x80
+#define BF_AIC_CCR_RFLUSH(v) (((v) & 0x1) << 7)
+#define BFM_AIC_CCR_RFLUSH(v) BM_AIC_CCR_RFLUSH
+#define BF_AIC_CCR_RFLUSH_V(e) BF_AIC_CCR_RFLUSH(BV_AIC_CCR_RFLUSH__##e)
+#define BFM_AIC_CCR_RFLUSH_V(v) BM_AIC_CCR_RFLUSH
+#define BP_AIC_CCR_EROR 6
+#define BM_AIC_CCR_EROR 0x40
+#define BF_AIC_CCR_EROR(v) (((v) & 0x1) << 6)
+#define BFM_AIC_CCR_EROR(v) BM_AIC_CCR_EROR
+#define BF_AIC_CCR_EROR_V(e) BF_AIC_CCR_EROR(BV_AIC_CCR_EROR__##e)
+#define BFM_AIC_CCR_EROR_V(v) BM_AIC_CCR_EROR
+#define BP_AIC_CCR_ETUR 5
+#define BM_AIC_CCR_ETUR 0x20
+#define BF_AIC_CCR_ETUR(v) (((v) & 0x1) << 5)
+#define BFM_AIC_CCR_ETUR(v) BM_AIC_CCR_ETUR
+#define BF_AIC_CCR_ETUR_V(e) BF_AIC_CCR_ETUR(BV_AIC_CCR_ETUR__##e)
+#define BFM_AIC_CCR_ETUR_V(v) BM_AIC_CCR_ETUR
+#define BP_AIC_CCR_ERFS 4
+#define BM_AIC_CCR_ERFS 0x10
+#define BF_AIC_CCR_ERFS(v) (((v) & 0x1) << 4)
+#define BFM_AIC_CCR_ERFS(v) BM_AIC_CCR_ERFS
+#define BF_AIC_CCR_ERFS_V(e) BF_AIC_CCR_ERFS(BV_AIC_CCR_ERFS__##e)
+#define BFM_AIC_CCR_ERFS_V(v) BM_AIC_CCR_ERFS
+#define BP_AIC_CCR_ETFS 3
+#define BM_AIC_CCR_ETFS 0x8
+#define BF_AIC_CCR_ETFS(v) (((v) & 0x1) << 3)
+#define BFM_AIC_CCR_ETFS(v) BM_AIC_CCR_ETFS
+#define BF_AIC_CCR_ETFS_V(e) BF_AIC_CCR_ETFS(BV_AIC_CCR_ETFS__##e)
+#define BFM_AIC_CCR_ETFS_V(v) BM_AIC_CCR_ETFS
+#define BP_AIC_CCR_ENLBF 2
+#define BM_AIC_CCR_ENLBF 0x4
+#define BF_AIC_CCR_ENLBF(v) (((v) & 0x1) << 2)
+#define BFM_AIC_CCR_ENLBF(v) BM_AIC_CCR_ENLBF
+#define BF_AIC_CCR_ENLBF_V(e) BF_AIC_CCR_ENLBF(BV_AIC_CCR_ENLBF__##e)
+#define BFM_AIC_CCR_ENLBF_V(v) BM_AIC_CCR_ENLBF
+#define BP_AIC_CCR_ERPL 1
+#define BM_AIC_CCR_ERPL 0x2
+#define BF_AIC_CCR_ERPL(v) (((v) & 0x1) << 1)
+#define BFM_AIC_CCR_ERPL(v) BM_AIC_CCR_ERPL
+#define BF_AIC_CCR_ERPL_V(e) BF_AIC_CCR_ERPL(BV_AIC_CCR_ERPL__##e)
+#define BFM_AIC_CCR_ERPL_V(v) BM_AIC_CCR_ERPL
+#define BP_AIC_CCR_EREC 0
+#define BM_AIC_CCR_EREC 0x1
+#define BF_AIC_CCR_EREC(v) (((v) & 0x1) << 0)
+#define BFM_AIC_CCR_EREC(v) BM_AIC_CCR_EREC
+#define BF_AIC_CCR_EREC_V(e) BF_AIC_CCR_EREC(BV_AIC_CCR_EREC__##e)
+#define BFM_AIC_CCR_EREC_V(v) BM_AIC_CCR_EREC
+
+#define REG_AIC_I2SCR jz_reg(AIC_I2SCR)
+#define JA_AIC_I2SCR (0xb0020000 + 0x10)
+#define JT_AIC_I2SCR JIO_32_RW
+#define JN_AIC_I2SCR AIC_I2SCR
+#define JI_AIC_I2SCR
+#define BP_AIC_I2SCR_RFIRST 17
+#define BM_AIC_I2SCR_RFIRST 0x20000
+#define BF_AIC_I2SCR_RFIRST(v) (((v) & 0x1) << 17)
+#define BFM_AIC_I2SCR_RFIRST(v) BM_AIC_I2SCR_RFIRST
+#define BF_AIC_I2SCR_RFIRST_V(e) BF_AIC_I2SCR_RFIRST(BV_AIC_I2SCR_RFIRST__##e)
+#define BFM_AIC_I2SCR_RFIRST_V(v) BM_AIC_I2SCR_RFIRST
+#define BP_AIC_I2SCR_SWLH 16
+#define BM_AIC_I2SCR_SWLH 0x10000
+#define BF_AIC_I2SCR_SWLH(v) (((v) & 0x1) << 16)
+#define BFM_AIC_I2SCR_SWLH(v) BM_AIC_I2SCR_SWLH
+#define BF_AIC_I2SCR_SWLH_V(e) BF_AIC_I2SCR_SWLH(BV_AIC_I2SCR_SWLH__##e)
+#define BFM_AIC_I2SCR_SWLH_V(v) BM_AIC_I2SCR_SWLH
+#define BP_AIC_I2SCR_ISTPBK 13
+#define BM_AIC_I2SCR_ISTPBK 0x2000
+#define BF_AIC_I2SCR_ISTPBK(v) (((v) & 0x1) << 13)
+#define BFM_AIC_I2SCR_ISTPBK(v) BM_AIC_I2SCR_ISTPBK
+#define BF_AIC_I2SCR_ISTPBK_V(e) BF_AIC_I2SCR_ISTPBK(BV_AIC_I2SCR_ISTPBK__##e)
+#define BFM_AIC_I2SCR_ISTPBK_V(v) BM_AIC_I2SCR_ISTPBK
+#define BP_AIC_I2SCR_STPBK 12
+#define BM_AIC_I2SCR_STPBK 0x1000
+#define BF_AIC_I2SCR_STPBK(v) (((v) & 0x1) << 12)
+#define BFM_AIC_I2SCR_STPBK(v) BM_AIC_I2SCR_STPBK
+#define BF_AIC_I2SCR_STPBK_V(e) BF_AIC_I2SCR_STPBK(BV_AIC_I2SCR_STPBK__##e)
+#define BFM_AIC_I2SCR_STPBK_V(v) BM_AIC_I2SCR_STPBK
+#define BP_AIC_I2SCR_ESCLK 4
+#define BM_AIC_I2SCR_ESCLK 0x10
+#define BF_AIC_I2SCR_ESCLK(v) (((v) & 0x1) << 4)
+#define BFM_AIC_I2SCR_ESCLK(v) BM_AIC_I2SCR_ESCLK
+#define BF_AIC_I2SCR_ESCLK_V(e) BF_AIC_I2SCR_ESCLK(BV_AIC_I2SCR_ESCLK__##e)
+#define BFM_AIC_I2SCR_ESCLK_V(v) BM_AIC_I2SCR_ESCLK
+#define BP_AIC_I2SCR_AMSL 0
+#define BM_AIC_I2SCR_AMSL 0x1
+#define BF_AIC_I2SCR_AMSL(v) (((v) & 0x1) << 0)
+#define BFM_AIC_I2SCR_AMSL(v) BM_AIC_I2SCR_AMSL
+#define BF_AIC_I2SCR_AMSL_V(e) BF_AIC_I2SCR_AMSL(BV_AIC_I2SCR_AMSL__##e)
+#define BFM_AIC_I2SCR_AMSL_V(v) BM_AIC_I2SCR_AMSL
+
+#define REG_AIC_SR jz_reg(AIC_SR)
+#define JA_AIC_SR (0xb0020000 + 0x14)
+#define JT_AIC_SR JIO_32_RW
+#define JN_AIC_SR AIC_SR
+#define JI_AIC_SR
+#define BP_AIC_SR_RFL 24
+#define BM_AIC_SR_RFL 0x3f000000
+#define BF_AIC_SR_RFL(v) (((v) & 0x3f) << 24)
+#define BFM_AIC_SR_RFL(v) BM_AIC_SR_RFL
+#define BF_AIC_SR_RFL_V(e) BF_AIC_SR_RFL(BV_AIC_SR_RFL__##e)
+#define BFM_AIC_SR_RFL_V(v) BM_AIC_SR_RFL
+#define BP_AIC_SR_TFL 8
+#define BM_AIC_SR_TFL 0x3f00
+#define BF_AIC_SR_TFL(v) (((v) & 0x3f) << 8)
+#define BFM_AIC_SR_TFL(v) BM_AIC_SR_TFL
+#define BF_AIC_SR_TFL_V(e) BF_AIC_SR_TFL(BV_AIC_SR_TFL__##e)
+#define BFM_AIC_SR_TFL_V(v) BM_AIC_SR_TFL
+#define BP_AIC_SR_ROR 6
+#define BM_AIC_SR_ROR 0x40
+#define BF_AIC_SR_ROR(v) (((v) & 0x1) << 6)
+#define BFM_AIC_SR_ROR(v) BM_AIC_SR_ROR
+#define BF_AIC_SR_ROR_V(e) BF_AIC_SR_ROR(BV_AIC_SR_ROR__##e)
+#define BFM_AIC_SR_ROR_V(v) BM_AIC_SR_ROR
+#define BP_AIC_SR_TUR 5
+#define BM_AIC_SR_TUR 0x20
+#define BF_AIC_SR_TUR(v) (((v) & 0x1) << 5)
+#define BFM_AIC_SR_TUR(v) BM_AIC_SR_TUR
+#define BF_AIC_SR_TUR_V(e) BF_AIC_SR_TUR(BV_AIC_SR_TUR__##e)
+#define BFM_AIC_SR_TUR_V(v) BM_AIC_SR_TUR
+#define BP_AIC_SR_RFS 4
+#define BM_AIC_SR_RFS 0x10
+#define BF_AIC_SR_RFS(v) (((v) & 0x1) << 4)
+#define BFM_AIC_SR_RFS(v) BM_AIC_SR_RFS
+#define BF_AIC_SR_RFS_V(e) BF_AIC_SR_RFS(BV_AIC_SR_RFS__##e)
+#define BFM_AIC_SR_RFS_V(v) BM_AIC_SR_RFS
+#define BP_AIC_SR_TFS 3
+#define BM_AIC_SR_TFS 0x8
+#define BF_AIC_SR_TFS(v) (((v) & 0x1) << 3)
+#define BFM_AIC_SR_TFS(v) BM_AIC_SR_TFS
+#define BF_AIC_SR_TFS_V(e) BF_AIC_SR_TFS(BV_AIC_SR_TFS__##e)
+#define BFM_AIC_SR_TFS_V(v) BM_AIC_SR_TFS
+
+#define REG_AIC_I2SSR jz_reg(AIC_I2SSR)
+#define JA_AIC_I2SSR (0xb0020000 + 0x1c)
+#define JT_AIC_I2SSR JIO_32_RW
+#define JN_AIC_I2SSR AIC_I2SSR
+#define JI_AIC_I2SSR
+#define BP_AIC_I2SSR_CHBSY 5
+#define BM_AIC_I2SSR_CHBSY 0x20
+#define BF_AIC_I2SSR_CHBSY(v) (((v) & 0x1) << 5)
+#define BFM_AIC_I2SSR_CHBSY(v) BM_AIC_I2SSR_CHBSY
+#define BF_AIC_I2SSR_CHBSY_V(e) BF_AIC_I2SSR_CHBSY(BV_AIC_I2SSR_CHBSY__##e)
+#define BFM_AIC_I2SSR_CHBSY_V(v) BM_AIC_I2SSR_CHBSY
+#define BP_AIC_I2SSR_TBSY 4
+#define BM_AIC_I2SSR_TBSY 0x10
+#define BF_AIC_I2SSR_TBSY(v) (((v) & 0x1) << 4)
+#define BFM_AIC_I2SSR_TBSY(v) BM_AIC_I2SSR_TBSY
+#define BF_AIC_I2SSR_TBSY_V(e) BF_AIC_I2SSR_TBSY(BV_AIC_I2SSR_TBSY__##e)
+#define BFM_AIC_I2SSR_TBSY_V(v) BM_AIC_I2SSR_TBSY
+#define BP_AIC_I2SSR_RBSY 3
+#define BM_AIC_I2SSR_RBSY 0x8
+#define BF_AIC_I2SSR_RBSY(v) (((v) & 0x1) << 3)
+#define BFM_AIC_I2SSR_RBSY(v) BM_AIC_I2SSR_RBSY
+#define BF_AIC_I2SSR_RBSY_V(e) BF_AIC_I2SSR_RBSY(BV_AIC_I2SSR_RBSY__##e)
+#define BFM_AIC_I2SSR_RBSY_V(v) BM_AIC_I2SSR_RBSY
+#define BP_AIC_I2SSR_BSY 2
+#define BM_AIC_I2SSR_BSY 0x4
+#define BF_AIC_I2SSR_BSY(v) (((v) & 0x1) << 2)
+#define BFM_AIC_I2SSR_BSY(v) BM_AIC_I2SSR_BSY
+#define BF_AIC_I2SSR_BSY_V(e) BF_AIC_I2SSR_BSY(BV_AIC_I2SSR_BSY__##e)
+#define BFM_AIC_I2SSR_BSY_V(v) BM_AIC_I2SSR_BSY
+
+#define REG_AIC_I2SDIV jz_reg(AIC_I2SDIV)
+#define JA_AIC_I2SDIV (0xb0020000 + 0x30)
+#define JT_AIC_I2SDIV JIO_32_RW
+#define JN_AIC_I2SDIV AIC_I2SDIV
+#define JI_AIC_I2SDIV
+
+#define REG_AIC_DR jz_reg(AIC_DR)
+#define JA_AIC_DR (0xb0020000 + 0x34)
+#define JT_AIC_DR JIO_32_RW
+#define JN_AIC_DR AIC_DR
+#define JI_AIC_DR
+
+#endif /* __HEADERGEN_AIC_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/cpm.h b/firmware/target/mips/ingenic_x1000/x1000/cpm.h
new file mode 100644
index 0000000000..752d270f20
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/cpm.h
@@ -0,0 +1,896 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_CPM_H__
+#define __HEADERGEN_CPM_H__
+
+#include "macro.h"
+
+#define REG_CPM_CCR jz_reg(CPM_CCR)
+#define JA_CPM_CCR (0xb0000000 + 0x0)
+#define JT_CPM_CCR JIO_32_RW
+#define JN_CPM_CCR CPM_CCR
+#define JI_CPM_CCR
+#define BP_CPM_CCR_SEL_SRC 30
+#define BM_CPM_CCR_SEL_SRC 0xc0000000
+#define BV_CPM_CCR_SEL_SRC__STOP 0x0
+#define BV_CPM_CCR_SEL_SRC__EXCLK 0x1
+#define BV_CPM_CCR_SEL_SRC__APLL 0x2
+#define BF_CPM_CCR_SEL_SRC(v) (((v) & 0x3) << 30)
+#define BFM_CPM_CCR_SEL_SRC(v) BM_CPM_CCR_SEL_SRC
+#define BF_CPM_CCR_SEL_SRC_V(e) BF_CPM_CCR_SEL_SRC(BV_CPM_CCR_SEL_SRC__##e)
+#define BFM_CPM_CCR_SEL_SRC_V(v) BM_CPM_CCR_SEL_SRC
+#define BP_CPM_CCR_SEL_CPLL 28
+#define BM_CPM_CCR_SEL_CPLL 0x30000000
+#define BV_CPM_CCR_SEL_CPLL__STOP 0x0
+#define BV_CPM_CCR_SEL_CPLL__SCLK_A 0x1
+#define BV_CPM_CCR_SEL_CPLL__MPLL 0x2
+#define BF_CPM_CCR_SEL_CPLL(v) (((v) & 0x3) << 28)
+#define BFM_CPM_CCR_SEL_CPLL(v) BM_CPM_CCR_SEL_CPLL
+#define BF_CPM_CCR_SEL_CPLL_V(e) BF_CPM_CCR_SEL_CPLL(BV_CPM_CCR_SEL_CPLL__##e)
+#define BFM_CPM_CCR_SEL_CPLL_V(v) BM_CPM_CCR_SEL_CPLL
+#define BP_CPM_CCR_SEL_H0PLL 26
+#define BM_CPM_CCR_SEL_H0PLL 0xc000000
+#define BV_CPM_CCR_SEL_H0PLL__STOP 0x0
+#define BV_CPM_CCR_SEL_H0PLL__SCLK_A 0x1
+#define BV_CPM_CCR_SEL_H0PLL__MPLL 0x2
+#define BF_CPM_CCR_SEL_H0PLL(v) (((v) & 0x3) << 26)
+#define BFM_CPM_CCR_SEL_H0PLL(v) BM_CPM_CCR_SEL_H0PLL
+#define BF_CPM_CCR_SEL_H0PLL_V(e) BF_CPM_CCR_SEL_H0PLL(BV_CPM_CCR_SEL_H0PLL__##e)
+#define BFM_CPM_CCR_SEL_H0PLL_V(v) BM_CPM_CCR_SEL_H0PLL
+#define BP_CPM_CCR_SEL_H2PLL 24
+#define BM_CPM_CCR_SEL_H2PLL 0x3000000
+#define BV_CPM_CCR_SEL_H2PLL__STOP 0x0
+#define BV_CPM_CCR_SEL_H2PLL__SCLK_A 0x1
+#define BV_CPM_CCR_SEL_H2PLL__MPLL 0x2
+#define BF_CPM_CCR_SEL_H2PLL(v) (((v) & 0x3) << 24)
+#define BFM_CPM_CCR_SEL_H2PLL(v) BM_CPM_CCR_SEL_H2PLL
+#define BF_CPM_CCR_SEL_H2PLL_V(e) BF_CPM_CCR_SEL_H2PLL(BV_CPM_CCR_SEL_H2PLL__##e)
+#define BFM_CPM_CCR_SEL_H2PLL_V(v) BM_CPM_CCR_SEL_H2PLL
+#define BP_CPM_CCR_PDIV 16
+#define BM_CPM_CCR_PDIV 0xf0000
+#define BF_CPM_CCR_PDIV(v) (((v) & 0xf) << 16)
+#define BFM_CPM_CCR_PDIV(v) BM_CPM_CCR_PDIV
+#define BF_CPM_CCR_PDIV_V(e) BF_CPM_CCR_PDIV(BV_CPM_CCR_PDIV__##e)
+#define BFM_CPM_CCR_PDIV_V(v) BM_CPM_CCR_PDIV
+#define BP_CPM_CCR_H2DIV 12
+#define BM_CPM_CCR_H2DIV 0xf000
+#define BF_CPM_CCR_H2DIV(v) (((v) & 0xf) << 12)
+#define BFM_CPM_CCR_H2DIV(v) BM_CPM_CCR_H2DIV
+#define BF_CPM_CCR_H2DIV_V(e) BF_CPM_CCR_H2DIV(BV_CPM_CCR_H2DIV__##e)
+#define BFM_CPM_CCR_H2DIV_V(v) BM_CPM_CCR_H2DIV
+#define BP_CPM_CCR_H0DIV 8
+#define BM_CPM_CCR_H0DIV 0xf00
+#define BF_CPM_CCR_H0DIV(v) (((v) & 0xf) << 8)
+#define BFM_CPM_CCR_H0DIV(v) BM_CPM_CCR_H0DIV
+#define BF_CPM_CCR_H0DIV_V(e) BF_CPM_CCR_H0DIV(BV_CPM_CCR_H0DIV__##e)
+#define BFM_CPM_CCR_H0DIV_V(v) BM_CPM_CCR_H0DIV
+#define BP_CPM_CCR_L2DIV 4
+#define BM_CPM_CCR_L2DIV 0xf0
+#define BF_CPM_CCR_L2DIV(v) (((v) & 0xf) << 4)
+#define BFM_CPM_CCR_L2DIV(v) BM_CPM_CCR_L2DIV
+#define BF_CPM_CCR_L2DIV_V(e) BF_CPM_CCR_L2DIV(BV_CPM_CCR_L2DIV__##e)
+#define BFM_CPM_CCR_L2DIV_V(v) BM_CPM_CCR_L2DIV
+#define BP_CPM_CCR_CDIV 0
+#define BM_CPM_CCR_CDIV 0xf
+#define BF_CPM_CCR_CDIV(v) (((v) & 0xf) << 0)
+#define BFM_CPM_CCR_CDIV(v) BM_CPM_CCR_CDIV
+#define BF_CPM_CCR_CDIV_V(e) BF_CPM_CCR_CDIV(BV_CPM_CCR_CDIV__##e)
+#define BFM_CPM_CCR_CDIV_V(v) BM_CPM_CCR_CDIV
+#define BP_CPM_CCR_GATE_SCLKA 23
+#define BM_CPM_CCR_GATE_SCLKA 0x800000
+#define BF_CPM_CCR_GATE_SCLKA(v) (((v) & 0x1) << 23)
+#define BFM_CPM_CCR_GATE_SCLKA(v) BM_CPM_CCR_GATE_SCLKA
+#define BF_CPM_CCR_GATE_SCLKA_V(e) BF_CPM_CCR_GATE_SCLKA(BV_CPM_CCR_GATE_SCLKA__##e)
+#define BFM_CPM_CCR_GATE_SCLKA_V(v) BM_CPM_CCR_GATE_SCLKA
+#define BP_CPM_CCR_CE_CPU 22
+#define BM_CPM_CCR_CE_CPU 0x400000
+#define BF_CPM_CCR_CE_CPU(v) (((v) & 0x1) << 22)
+#define BFM_CPM_CCR_CE_CPU(v) BM_CPM_CCR_CE_CPU
+#define BF_CPM_CCR_CE_CPU_V(e) BF_CPM_CCR_CE_CPU(BV_CPM_CCR_CE_CPU__##e)
+#define BFM_CPM_CCR_CE_CPU_V(v) BM_CPM_CCR_CE_CPU
+#define BP_CPM_CCR_CE_AHB0 21
+#define BM_CPM_CCR_CE_AHB0 0x200000
+#define BF_CPM_CCR_CE_AHB0(v) (((v) & 0x1) << 21)
+#define BFM_CPM_CCR_CE_AHB0(v) BM_CPM_CCR_CE_AHB0
+#define BF_CPM_CCR_CE_AHB0_V(e) BF_CPM_CCR_CE_AHB0(BV_CPM_CCR_CE_AHB0__##e)
+#define BFM_CPM_CCR_CE_AHB0_V(v) BM_CPM_CCR_CE_AHB0
+#define BP_CPM_CCR_CE_AHB2 20
+#define BM_CPM_CCR_CE_AHB2 0x100000
+#define BF_CPM_CCR_CE_AHB2(v) (((v) & 0x1) << 20)
+#define BFM_CPM_CCR_CE_AHB2(v) BM_CPM_CCR_CE_AHB2
+#define BF_CPM_CCR_CE_AHB2_V(e) BF_CPM_CCR_CE_AHB2(BV_CPM_CCR_CE_AHB2__##e)
+#define BFM_CPM_CCR_CE_AHB2_V(v) BM_CPM_CCR_CE_AHB2
+
+#define REG_CPM_CSR jz_reg(CPM_CSR)
+#define JA_CPM_CSR (0xb0000000 + 0xd4)
+#define JT_CPM_CSR JIO_32_RW
+#define JN_CPM_CSR CPM_CSR
+#define JI_CPM_CSR
+#define BP_CPM_CSR_SRC_MUX 31
+#define BM_CPM_CSR_SRC_MUX 0x80000000
+#define BF_CPM_CSR_SRC_MUX(v) (((v) & 0x1) << 31)
+#define BFM_CPM_CSR_SRC_MUX(v) BM_CPM_CSR_SRC_MUX
+#define BF_CPM_CSR_SRC_MUX_V(e) BF_CPM_CSR_SRC_MUX(BV_CPM_CSR_SRC_MUX__##e)
+#define BFM_CPM_CSR_SRC_MUX_V(v) BM_CPM_CSR_SRC_MUX
+#define BP_CPM_CSR_CPU_MUX 30
+#define BM_CPM_CSR_CPU_MUX 0x40000000
+#define BF_CPM_CSR_CPU_MUX(v) (((v) & 0x1) << 30)
+#define BFM_CPM_CSR_CPU_MUX(v) BM_CPM_CSR_CPU_MUX
+#define BF_CPM_CSR_CPU_MUX_V(e) BF_CPM_CSR_CPU_MUX(BV_CPM_CSR_CPU_MUX__##e)
+#define BFM_CPM_CSR_CPU_MUX_V(v) BM_CPM_CSR_CPU_MUX
+#define BP_CPM_CSR_AHB0_MUX 29
+#define BM_CPM_CSR_AHB0_MUX 0x20000000
+#define BF_CPM_CSR_AHB0_MUX(v) (((v) & 0x1) << 29)
+#define BFM_CPM_CSR_AHB0_MUX(v) BM_CPM_CSR_AHB0_MUX
+#define BF_CPM_CSR_AHB0_MUX_V(e) BF_CPM_CSR_AHB0_MUX(BV_CPM_CSR_AHB0_MUX__##e)
+#define BFM_CPM_CSR_AHB0_MUX_V(v) BM_CPM_CSR_AHB0_MUX
+#define BP_CPM_CSR_AHB2_MUX 28
+#define BM_CPM_CSR_AHB2_MUX 0x10000000
+#define BF_CPM_CSR_AHB2_MUX(v) (((v) & 0x1) << 28)
+#define BFM_CPM_CSR_AHB2_MUX(v) BM_CPM_CSR_AHB2_MUX
+#define BF_CPM_CSR_AHB2_MUX_V(e) BF_CPM_CSR_AHB2_MUX(BV_CPM_CSR_AHB2_MUX__##e)
+#define BFM_CPM_CSR_AHB2_MUX_V(v) BM_CPM_CSR_AHB2_MUX
+#define BP_CPM_CSR_DDR_MUX 27
+#define BM_CPM_CSR_DDR_MUX 0x8000000
+#define BF_CPM_CSR_DDR_MUX(v) (((v) & 0x1) << 27)
+#define BFM_CPM_CSR_DDR_MUX(v) BM_CPM_CSR_DDR_MUX
+#define BF_CPM_CSR_DDR_MUX_V(e) BF_CPM_CSR_DDR_MUX(BV_CPM_CSR_DDR_MUX__##e)
+#define BFM_CPM_CSR_DDR_MUX_V(v) BM_CPM_CSR_DDR_MUX
+#define BP_CPM_CSR_H2DIV_BUSY 2
+#define BM_CPM_CSR_H2DIV_BUSY 0x4
+#define BF_CPM_CSR_H2DIV_BUSY(v) (((v) & 0x1) << 2)
+#define BFM_CPM_CSR_H2DIV_BUSY(v) BM_CPM_CSR_H2DIV_BUSY
+#define BF_CPM_CSR_H2DIV_BUSY_V(e) BF_CPM_CSR_H2DIV_BUSY(BV_CPM_CSR_H2DIV_BUSY__##e)
+#define BFM_CPM_CSR_H2DIV_BUSY_V(v) BM_CPM_CSR_H2DIV_BUSY
+#define BP_CPM_CSR_H0DIV_BUSY 1
+#define BM_CPM_CSR_H0DIV_BUSY 0x2
+#define BF_CPM_CSR_H0DIV_BUSY(v) (((v) & 0x1) << 1)
+#define BFM_CPM_CSR_H0DIV_BUSY(v) BM_CPM_CSR_H0DIV_BUSY
+#define BF_CPM_CSR_H0DIV_BUSY_V(e) BF_CPM_CSR_H0DIV_BUSY(BV_CPM_CSR_H0DIV_BUSY__##e)
+#define BFM_CPM_CSR_H0DIV_BUSY_V(v) BM_CPM_CSR_H0DIV_BUSY
+#define BP_CPM_CSR_CDIV_BUSY 0
+#define BM_CPM_CSR_CDIV_BUSY 0x1
+#define BF_CPM_CSR_CDIV_BUSY(v) (((v) & 0x1) << 0)
+#define BFM_CPM_CSR_CDIV_BUSY(v) BM_CPM_CSR_CDIV_BUSY
+#define BF_CPM_CSR_CDIV_BUSY_V(e) BF_CPM_CSR_CDIV_BUSY(BV_CPM_CSR_CDIV_BUSY__##e)
+#define BFM_CPM_CSR_CDIV_BUSY_V(v) BM_CPM_CSR_CDIV_BUSY
+
+#define REG_CPM_DDRCDR jz_reg(CPM_DDRCDR)
+#define JA_CPM_DDRCDR (0xb0000000 + 0x2c)
+#define JT_CPM_DDRCDR JIO_32_RW
+#define JN_CPM_DDRCDR CPM_DDRCDR
+#define JI_CPM_DDRCDR
+#define BP_CPM_DDRCDR_CLKSRC 30
+#define BM_CPM_DDRCDR_CLKSRC 0xc0000000
+#define BV_CPM_DDRCDR_CLKSRC__STOP 0x0
+#define BV_CPM_DDRCDR_CLKSRC__SCLK_A 0x1
+#define BV_CPM_DDRCDR_CLKSRC__MPLL 0x2
+#define BF_CPM_DDRCDR_CLKSRC(v) (((v) & 0x3) << 30)
+#define BFM_CPM_DDRCDR_CLKSRC(v) BM_CPM_DDRCDR_CLKSRC
+#define BF_CPM_DDRCDR_CLKSRC_V(e) BF_CPM_DDRCDR_CLKSRC(BV_CPM_DDRCDR_CLKSRC__##e)
+#define BFM_CPM_DDRCDR_CLKSRC_V(v) BM_CPM_DDRCDR_CLKSRC
+#define BP_CPM_DDRCDR_CLKDIV 0
+#define BM_CPM_DDRCDR_CLKDIV 0xf
+#define BF_CPM_DDRCDR_CLKDIV(v) (((v) & 0xf) << 0)
+#define BFM_CPM_DDRCDR_CLKDIV(v) BM_CPM_DDRCDR_CLKDIV
+#define BF_CPM_DDRCDR_CLKDIV_V(e) BF_CPM_DDRCDR_CLKDIV(BV_CPM_DDRCDR_CLKDIV__##e)
+#define BFM_CPM_DDRCDR_CLKDIV_V(v) BM_CPM_DDRCDR_CLKDIV
+#define BP_CPM_DDRCDR_CE 29
+#define BM_CPM_DDRCDR_CE 0x20000000
+#define BF_CPM_DDRCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_DDRCDR_CE(v) BM_CPM_DDRCDR_CE
+#define BF_CPM_DDRCDR_CE_V(e) BF_CPM_DDRCDR_CE(BV_CPM_DDRCDR_CE__##e)
+#define BFM_CPM_DDRCDR_CE_V(v) BM_CPM_DDRCDR_CE
+#define BP_CPM_DDRCDR_BUSY 28
+#define BM_CPM_DDRCDR_BUSY 0x10000000
+#define BF_CPM_DDRCDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_DDRCDR_BUSY(v) BM_CPM_DDRCDR_BUSY
+#define BF_CPM_DDRCDR_BUSY_V(e) BF_CPM_DDRCDR_BUSY(BV_CPM_DDRCDR_BUSY__##e)
+#define BFM_CPM_DDRCDR_BUSY_V(v) BM_CPM_DDRCDR_BUSY
+#define BP_CPM_DDRCDR_STOP 27
+#define BM_CPM_DDRCDR_STOP 0x8000000
+#define BF_CPM_DDRCDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_DDRCDR_STOP(v) BM_CPM_DDRCDR_STOP
+#define BF_CPM_DDRCDR_STOP_V(e) BF_CPM_DDRCDR_STOP(BV_CPM_DDRCDR_STOP__##e)
+#define BFM_CPM_DDRCDR_STOP_V(v) BM_CPM_DDRCDR_STOP
+#define BP_CPM_DDRCDR_GATE_EN 26
+#define BM_CPM_DDRCDR_GATE_EN 0x4000000
+#define BF_CPM_DDRCDR_GATE_EN(v) (((v) & 0x1) << 26)
+#define BFM_CPM_DDRCDR_GATE_EN(v) BM_CPM_DDRCDR_GATE_EN
+#define BF_CPM_DDRCDR_GATE_EN_V(e) BF_CPM_DDRCDR_GATE_EN(BV_CPM_DDRCDR_GATE_EN__##e)
+#define BFM_CPM_DDRCDR_GATE_EN_V(v) BM_CPM_DDRCDR_GATE_EN
+#define BP_CPM_DDRCDR_CHANGE_EN 25
+#define BM_CPM_DDRCDR_CHANGE_EN 0x2000000
+#define BF_CPM_DDRCDR_CHANGE_EN(v) (((v) & 0x1) << 25)
+#define BFM_CPM_DDRCDR_CHANGE_EN(v) BM_CPM_DDRCDR_CHANGE_EN
+#define BF_CPM_DDRCDR_CHANGE_EN_V(e) BF_CPM_DDRCDR_CHANGE_EN(BV_CPM_DDRCDR_CHANGE_EN__##e)
+#define BFM_CPM_DDRCDR_CHANGE_EN_V(v) BM_CPM_DDRCDR_CHANGE_EN
+#define BP_CPM_DDRCDR_FLAG 24
+#define BM_CPM_DDRCDR_FLAG 0x1000000
+#define BF_CPM_DDRCDR_FLAG(v) (((v) & 0x1) << 24)
+#define BFM_CPM_DDRCDR_FLAG(v) BM_CPM_DDRCDR_FLAG
+#define BF_CPM_DDRCDR_FLAG_V(e) BF_CPM_DDRCDR_FLAG(BV_CPM_DDRCDR_FLAG__##e)
+#define BFM_CPM_DDRCDR_FLAG_V(v) BM_CPM_DDRCDR_FLAG
+
+#define REG_CPM_I2SCDR jz_reg(CPM_I2SCDR)
+#define JA_CPM_I2SCDR (0xb0000000 + 0x60)
+#define JT_CPM_I2SCDR JIO_32_RW
+#define JN_CPM_I2SCDR CPM_I2SCDR
+#define JI_CPM_I2SCDR
+#define BP_CPM_I2SCDR_DIV_M 13
+#define BM_CPM_I2SCDR_DIV_M 0x3fe000
+#define BF_CPM_I2SCDR_DIV_M(v) (((v) & 0x1ff) << 13)
+#define BFM_CPM_I2SCDR_DIV_M(v) BM_CPM_I2SCDR_DIV_M
+#define BF_CPM_I2SCDR_DIV_M_V(e) BF_CPM_I2SCDR_DIV_M(BV_CPM_I2SCDR_DIV_M__##e)
+#define BFM_CPM_I2SCDR_DIV_M_V(v) BM_CPM_I2SCDR_DIV_M
+#define BP_CPM_I2SCDR_DIV_N 0
+#define BM_CPM_I2SCDR_DIV_N 0x1fff
+#define BF_CPM_I2SCDR_DIV_N(v) (((v) & 0x1fff) << 0)
+#define BFM_CPM_I2SCDR_DIV_N(v) BM_CPM_I2SCDR_DIV_N
+#define BF_CPM_I2SCDR_DIV_N_V(e) BF_CPM_I2SCDR_DIV_N(BV_CPM_I2SCDR_DIV_N__##e)
+#define BFM_CPM_I2SCDR_DIV_N_V(v) BM_CPM_I2SCDR_DIV_N
+#define BP_CPM_I2SCDR_PCS 31
+#define BM_CPM_I2SCDR_PCS 0x80000000
+#define BV_CPM_I2SCDR_PCS__SCLK_A 0x0
+#define BV_CPM_I2SCDR_PCS__MPLL 0x1
+#define BF_CPM_I2SCDR_PCS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_I2SCDR_PCS(v) BM_CPM_I2SCDR_PCS
+#define BF_CPM_I2SCDR_PCS_V(e) BF_CPM_I2SCDR_PCS(BV_CPM_I2SCDR_PCS__##e)
+#define BFM_CPM_I2SCDR_PCS_V(v) BM_CPM_I2SCDR_PCS
+#define BP_CPM_I2SCDR_CS 30
+#define BM_CPM_I2SCDR_CS 0x40000000
+#define BV_CPM_I2SCDR_CS__EXCLK 0x0
+#define BV_CPM_I2SCDR_CS__PLL 0x1
+#define BF_CPM_I2SCDR_CS(v) (((v) & 0x1) << 30)
+#define BFM_CPM_I2SCDR_CS(v) BM_CPM_I2SCDR_CS
+#define BF_CPM_I2SCDR_CS_V(e) BF_CPM_I2SCDR_CS(BV_CPM_I2SCDR_CS__##e)
+#define BFM_CPM_I2SCDR_CS_V(v) BM_CPM_I2SCDR_CS
+#define BP_CPM_I2SCDR_CE 29
+#define BM_CPM_I2SCDR_CE 0x20000000
+#define BF_CPM_I2SCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_I2SCDR_CE(v) BM_CPM_I2SCDR_CE
+#define BF_CPM_I2SCDR_CE_V(e) BF_CPM_I2SCDR_CE(BV_CPM_I2SCDR_CE__##e)
+#define BFM_CPM_I2SCDR_CE_V(v) BM_CPM_I2SCDR_CE
+
+#define REG_CPM_I2SCDR1 jz_reg(CPM_I2SCDR1)
+#define JA_CPM_I2SCDR1 (0xb0000000 + 0x70)
+#define JT_CPM_I2SCDR1 JIO_32_RW
+#define JN_CPM_I2SCDR1 CPM_I2SCDR1
+#define JI_CPM_I2SCDR1
+
+#define REG_CPM_LPCDR jz_reg(CPM_LPCDR)
+#define JA_CPM_LPCDR (0xb0000000 + 0x64)
+#define JT_CPM_LPCDR JIO_32_RW
+#define JN_CPM_LPCDR CPM_LPCDR
+#define JI_CPM_LPCDR
+#define BP_CPM_LPCDR_CLKDIV 0
+#define BM_CPM_LPCDR_CLKDIV 0xff
+#define BF_CPM_LPCDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_LPCDR_CLKDIV(v) BM_CPM_LPCDR_CLKDIV
+#define BF_CPM_LPCDR_CLKDIV_V(e) BF_CPM_LPCDR_CLKDIV(BV_CPM_LPCDR_CLKDIV__##e)
+#define BFM_CPM_LPCDR_CLKDIV_V(v) BM_CPM_LPCDR_CLKDIV
+#define BP_CPM_LPCDR_CLKSRC 31
+#define BM_CPM_LPCDR_CLKSRC 0x80000000
+#define BV_CPM_LPCDR_CLKSRC__SCLK_A 0x0
+#define BV_CPM_LPCDR_CLKSRC__MPLL 0x1
+#define BF_CPM_LPCDR_CLKSRC(v) (((v) & 0x1) << 31)
+#define BFM_CPM_LPCDR_CLKSRC(v) BM_CPM_LPCDR_CLKSRC
+#define BF_CPM_LPCDR_CLKSRC_V(e) BF_CPM_LPCDR_CLKSRC(BV_CPM_LPCDR_CLKSRC__##e)
+#define BFM_CPM_LPCDR_CLKSRC_V(v) BM_CPM_LPCDR_CLKSRC
+#define BP_CPM_LPCDR_CE 28
+#define BM_CPM_LPCDR_CE 0x10000000
+#define BF_CPM_LPCDR_CE(v) (((v) & 0x1) << 28)
+#define BFM_CPM_LPCDR_CE(v) BM_CPM_LPCDR_CE
+#define BF_CPM_LPCDR_CE_V(e) BF_CPM_LPCDR_CE(BV_CPM_LPCDR_CE__##e)
+#define BFM_CPM_LPCDR_CE_V(v) BM_CPM_LPCDR_CE
+#define BP_CPM_LPCDR_BUSY 27
+#define BM_CPM_LPCDR_BUSY 0x8000000
+#define BF_CPM_LPCDR_BUSY(v) (((v) & 0x1) << 27)
+#define BFM_CPM_LPCDR_BUSY(v) BM_CPM_LPCDR_BUSY
+#define BF_CPM_LPCDR_BUSY_V(e) BF_CPM_LPCDR_BUSY(BV_CPM_LPCDR_BUSY__##e)
+#define BFM_CPM_LPCDR_BUSY_V(v) BM_CPM_LPCDR_BUSY
+#define BP_CPM_LPCDR_STOP 26
+#define BM_CPM_LPCDR_STOP 0x4000000
+#define BF_CPM_LPCDR_STOP(v) (((v) & 0x1) << 26)
+#define BFM_CPM_LPCDR_STOP(v) BM_CPM_LPCDR_STOP
+#define BF_CPM_LPCDR_STOP_V(e) BF_CPM_LPCDR_STOP(BV_CPM_LPCDR_STOP__##e)
+#define BFM_CPM_LPCDR_STOP_V(v) BM_CPM_LPCDR_STOP
+
+#define REG_CPM_MSC0CDR jz_reg(CPM_MSC0CDR)
+#define JA_CPM_MSC0CDR (0xb0000000 + 0x68)
+#define JT_CPM_MSC0CDR JIO_32_RW
+#define JN_CPM_MSC0CDR CPM_MSC0CDR
+#define JI_CPM_MSC0CDR
+#define BP_CPM_MSC0CDR_CLKDIV 0
+#define BM_CPM_MSC0CDR_CLKDIV 0xff
+#define BF_CPM_MSC0CDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_MSC0CDR_CLKDIV(v) BM_CPM_MSC0CDR_CLKDIV
+#define BF_CPM_MSC0CDR_CLKDIV_V(e) BF_CPM_MSC0CDR_CLKDIV(BV_CPM_MSC0CDR_CLKDIV__##e)
+#define BFM_CPM_MSC0CDR_CLKDIV_V(v) BM_CPM_MSC0CDR_CLKDIV
+#define BP_CPM_MSC0CDR_CLKSRC 31
+#define BM_CPM_MSC0CDR_CLKSRC 0x80000000
+#define BV_CPM_MSC0CDR_CLKSRC__SCLK_A 0x0
+#define BV_CPM_MSC0CDR_CLKSRC__MPLL 0x1
+#define BF_CPM_MSC0CDR_CLKSRC(v) (((v) & 0x1) << 31)
+#define BFM_CPM_MSC0CDR_CLKSRC(v) BM_CPM_MSC0CDR_CLKSRC
+#define BF_CPM_MSC0CDR_CLKSRC_V(e) BF_CPM_MSC0CDR_CLKSRC(BV_CPM_MSC0CDR_CLKSRC__##e)
+#define BFM_CPM_MSC0CDR_CLKSRC_V(v) BM_CPM_MSC0CDR_CLKSRC
+#define BP_CPM_MSC0CDR_CE 29
+#define BM_CPM_MSC0CDR_CE 0x20000000
+#define BF_CPM_MSC0CDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_MSC0CDR_CE(v) BM_CPM_MSC0CDR_CE
+#define BF_CPM_MSC0CDR_CE_V(e) BF_CPM_MSC0CDR_CE(BV_CPM_MSC0CDR_CE__##e)
+#define BFM_CPM_MSC0CDR_CE_V(v) BM_CPM_MSC0CDR_CE
+#define BP_CPM_MSC0CDR_BUSY 28
+#define BM_CPM_MSC0CDR_BUSY 0x10000000
+#define BF_CPM_MSC0CDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_MSC0CDR_BUSY(v) BM_CPM_MSC0CDR_BUSY
+#define BF_CPM_MSC0CDR_BUSY_V(e) BF_CPM_MSC0CDR_BUSY(BV_CPM_MSC0CDR_BUSY__##e)
+#define BFM_CPM_MSC0CDR_BUSY_V(v) BM_CPM_MSC0CDR_BUSY
+#define BP_CPM_MSC0CDR_STOP 27
+#define BM_CPM_MSC0CDR_STOP 0x8000000
+#define BF_CPM_MSC0CDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_MSC0CDR_STOP(v) BM_CPM_MSC0CDR_STOP
+#define BF_CPM_MSC0CDR_STOP_V(e) BF_CPM_MSC0CDR_STOP(BV_CPM_MSC0CDR_STOP__##e)
+#define BFM_CPM_MSC0CDR_STOP_V(v) BM_CPM_MSC0CDR_STOP
+#define BP_CPM_MSC0CDR_S_CLK0_SEL 15
+#define BM_CPM_MSC0CDR_S_CLK0_SEL 0x8000
+#define BV_CPM_MSC0CDR_S_CLK0_SEL__90DEG 0x0
+#define BV_CPM_MSC0CDR_S_CLK0_SEL__180DEG 0x1
+#define BF_CPM_MSC0CDR_S_CLK0_SEL(v) (((v) & 0x1) << 15)
+#define BFM_CPM_MSC0CDR_S_CLK0_SEL(v) BM_CPM_MSC0CDR_S_CLK0_SEL
+#define BF_CPM_MSC0CDR_S_CLK0_SEL_V(e) BF_CPM_MSC0CDR_S_CLK0_SEL(BV_CPM_MSC0CDR_S_CLK0_SEL__##e)
+#define BFM_CPM_MSC0CDR_S_CLK0_SEL_V(v) BM_CPM_MSC0CDR_S_CLK0_SEL
+
+#define REG_CPM_MSC1CDR jz_reg(CPM_MSC1CDR)
+#define JA_CPM_MSC1CDR (0xb0000000 + 0xa4)
+#define JT_CPM_MSC1CDR JIO_32_RW
+#define JN_CPM_MSC1CDR CPM_MSC1CDR
+#define JI_CPM_MSC1CDR
+#define BP_CPM_MSC1CDR_CLKDIV 0
+#define BM_CPM_MSC1CDR_CLKDIV 0xff
+#define BF_CPM_MSC1CDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_MSC1CDR_CLKDIV(v) BM_CPM_MSC1CDR_CLKDIV
+#define BF_CPM_MSC1CDR_CLKDIV_V(e) BF_CPM_MSC1CDR_CLKDIV(BV_CPM_MSC1CDR_CLKDIV__##e)
+#define BFM_CPM_MSC1CDR_CLKDIV_V(v) BM_CPM_MSC1CDR_CLKDIV
+#define BP_CPM_MSC1CDR_CE 29
+#define BM_CPM_MSC1CDR_CE 0x20000000
+#define BF_CPM_MSC1CDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_MSC1CDR_CE(v) BM_CPM_MSC1CDR_CE
+#define BF_CPM_MSC1CDR_CE_V(e) BF_CPM_MSC1CDR_CE(BV_CPM_MSC1CDR_CE__##e)
+#define BFM_CPM_MSC1CDR_CE_V(v) BM_CPM_MSC1CDR_CE
+#define BP_CPM_MSC1CDR_BUSY 28
+#define BM_CPM_MSC1CDR_BUSY 0x10000000
+#define BF_CPM_MSC1CDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_MSC1CDR_BUSY(v) BM_CPM_MSC1CDR_BUSY
+#define BF_CPM_MSC1CDR_BUSY_V(e) BF_CPM_MSC1CDR_BUSY(BV_CPM_MSC1CDR_BUSY__##e)
+#define BFM_CPM_MSC1CDR_BUSY_V(v) BM_CPM_MSC1CDR_BUSY
+#define BP_CPM_MSC1CDR_STOP 27
+#define BM_CPM_MSC1CDR_STOP 0x8000000
+#define BF_CPM_MSC1CDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_MSC1CDR_STOP(v) BM_CPM_MSC1CDR_STOP
+#define BF_CPM_MSC1CDR_STOP_V(e) BF_CPM_MSC1CDR_STOP(BV_CPM_MSC1CDR_STOP__##e)
+#define BFM_CPM_MSC1CDR_STOP_V(v) BM_CPM_MSC1CDR_STOP
+#define BP_CPM_MSC1CDR_S_CLK1_SEL 15
+#define BM_CPM_MSC1CDR_S_CLK1_SEL 0x8000
+#define BV_CPM_MSC1CDR_S_CLK1_SEL__90DEG 0x0
+#define BV_CPM_MSC1CDR_S_CLK1_SEL__180DEG 0x1
+#define BF_CPM_MSC1CDR_S_CLK1_SEL(v) (((v) & 0x1) << 15)
+#define BFM_CPM_MSC1CDR_S_CLK1_SEL(v) BM_CPM_MSC1CDR_S_CLK1_SEL
+#define BF_CPM_MSC1CDR_S_CLK1_SEL_V(e) BF_CPM_MSC1CDR_S_CLK1_SEL(BV_CPM_MSC1CDR_S_CLK1_SEL__##e)
+#define BFM_CPM_MSC1CDR_S_CLK1_SEL_V(v) BM_CPM_MSC1CDR_S_CLK1_SEL
+
+#define REG_CPM_SSICDR jz_reg(CPM_SSICDR)
+#define JA_CPM_SSICDR (0xb0000000 + 0x74)
+#define JT_CPM_SSICDR JIO_32_RW
+#define JN_CPM_SSICDR CPM_SSICDR
+#define JI_CPM_SSICDR
+#define BP_CPM_SSICDR_CLKDIV 0
+#define BM_CPM_SSICDR_CLKDIV 0xff
+#define BF_CPM_SSICDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_SSICDR_CLKDIV(v) BM_CPM_SSICDR_CLKDIV
+#define BF_CPM_SSICDR_CLKDIV_V(e) BF_CPM_SSICDR_CLKDIV(BV_CPM_SSICDR_CLKDIV__##e)
+#define BFM_CPM_SSICDR_CLKDIV_V(v) BM_CPM_SSICDR_CLKDIV
+#define BP_CPM_SSICDR_SFC_CS 31
+#define BM_CPM_SSICDR_SFC_CS 0x80000000
+#define BV_CPM_SSICDR_SFC_CS__SCLK_A 0x0
+#define BV_CPM_SSICDR_SFC_CS__MPLL 0x1
+#define BF_CPM_SSICDR_SFC_CS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_SSICDR_SFC_CS(v) BM_CPM_SSICDR_SFC_CS
+#define BF_CPM_SSICDR_SFC_CS_V(e) BF_CPM_SSICDR_SFC_CS(BV_CPM_SSICDR_SFC_CS__##e)
+#define BFM_CPM_SSICDR_SFC_CS_V(v) BM_CPM_SSICDR_SFC_CS
+#define BP_CPM_SSICDR_SSI_CS 30
+#define BM_CPM_SSICDR_SSI_CS 0x40000000
+#define BV_CPM_SSICDR_SSI_CS__EXCLK 0x0
+#define BV_CPM_SSICDR_SSI_CS__HALF_SFC 0x1
+#define BF_CPM_SSICDR_SSI_CS(v) (((v) & 0x1) << 30)
+#define BFM_CPM_SSICDR_SSI_CS(v) BM_CPM_SSICDR_SSI_CS
+#define BF_CPM_SSICDR_SSI_CS_V(e) BF_CPM_SSICDR_SSI_CS(BV_CPM_SSICDR_SSI_CS__##e)
+#define BFM_CPM_SSICDR_SSI_CS_V(v) BM_CPM_SSICDR_SSI_CS
+#define BP_CPM_SSICDR_CE 29
+#define BM_CPM_SSICDR_CE 0x20000000
+#define BF_CPM_SSICDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_SSICDR_CE(v) BM_CPM_SSICDR_CE
+#define BF_CPM_SSICDR_CE_V(e) BF_CPM_SSICDR_CE(BV_CPM_SSICDR_CE__##e)
+#define BFM_CPM_SSICDR_CE_V(v) BM_CPM_SSICDR_CE
+#define BP_CPM_SSICDR_BUSY 28
+#define BM_CPM_SSICDR_BUSY 0x10000000
+#define BF_CPM_SSICDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_SSICDR_BUSY(v) BM_CPM_SSICDR_BUSY
+#define BF_CPM_SSICDR_BUSY_V(e) BF_CPM_SSICDR_BUSY(BV_CPM_SSICDR_BUSY__##e)
+#define BFM_CPM_SSICDR_BUSY_V(v) BM_CPM_SSICDR_BUSY
+#define BP_CPM_SSICDR_STOP 27
+#define BM_CPM_SSICDR_STOP 0x8000000
+#define BF_CPM_SSICDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_SSICDR_STOP(v) BM_CPM_SSICDR_STOP
+#define BF_CPM_SSICDR_STOP_V(e) BF_CPM_SSICDR_STOP(BV_CPM_SSICDR_STOP__##e)
+#define BFM_CPM_SSICDR_STOP_V(v) BM_CPM_SSICDR_STOP
+
+#define REG_CPM_DRCG jz_reg(CPM_DRCG)
+#define JA_CPM_DRCG (0xb0000000 + 0xd0)
+#define JT_CPM_DRCG JIO_32_RW
+#define JN_CPM_DRCG CPM_DRCG
+#define JI_CPM_DRCG
+
+#define REG_CPM_APCR jz_reg(CPM_APCR)
+#define JA_CPM_APCR (0xb0000000 + 0x10)
+#define JT_CPM_APCR JIO_32_RW
+#define JN_CPM_APCR CPM_APCR
+#define JI_CPM_APCR
+#define BP_CPM_APCR_PLLM 24
+#define BM_CPM_APCR_PLLM 0x7f000000
+#define BF_CPM_APCR_PLLM(v) (((v) & 0x7f) << 24)
+#define BFM_CPM_APCR_PLLM(v) BM_CPM_APCR_PLLM
+#define BF_CPM_APCR_PLLM_V(e) BF_CPM_APCR_PLLM(BV_CPM_APCR_PLLM__##e)
+#define BFM_CPM_APCR_PLLM_V(v) BM_CPM_APCR_PLLM
+#define BP_CPM_APCR_PLLN 18
+#define BM_CPM_APCR_PLLN 0x7c0000
+#define BF_CPM_APCR_PLLN(v) (((v) & 0x1f) << 18)
+#define BFM_CPM_APCR_PLLN(v) BM_CPM_APCR_PLLN
+#define BF_CPM_APCR_PLLN_V(e) BF_CPM_APCR_PLLN(BV_CPM_APCR_PLLN__##e)
+#define BFM_CPM_APCR_PLLN_V(v) BM_CPM_APCR_PLLN
+#define BP_CPM_APCR_PLLOD 16
+#define BM_CPM_APCR_PLLOD 0x30000
+#define BF_CPM_APCR_PLLOD(v) (((v) & 0x3) << 16)
+#define BFM_CPM_APCR_PLLOD(v) BM_CPM_APCR_PLLOD
+#define BF_CPM_APCR_PLLOD_V(e) BF_CPM_APCR_PLLOD(BV_CPM_APCR_PLLOD__##e)
+#define BFM_CPM_APCR_PLLOD_V(v) BM_CPM_APCR_PLLOD
+#define BP_CPM_APCR_PLLST 0
+#define BM_CPM_APCR_PLLST 0xff
+#define BF_CPM_APCR_PLLST(v) (((v) & 0xff) << 0)
+#define BFM_CPM_APCR_PLLST(v) BM_CPM_APCR_PLLST
+#define BF_CPM_APCR_PLLST_V(e) BF_CPM_APCR_PLLST(BV_CPM_APCR_PLLST__##e)
+#define BFM_CPM_APCR_PLLST_V(v) BM_CPM_APCR_PLLST
+#define BP_CPM_APCR_BS 31
+#define BM_CPM_APCR_BS 0x80000000
+#define BF_CPM_APCR_BS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_APCR_BS(v) BM_CPM_APCR_BS
+#define BF_CPM_APCR_BS_V(e) BF_CPM_APCR_BS(BV_CPM_APCR_BS__##e)
+#define BFM_CPM_APCR_BS_V(v) BM_CPM_APCR_BS
+#define BP_CPM_APCR_LOCK 15
+#define BM_CPM_APCR_LOCK 0x8000
+#define BF_CPM_APCR_LOCK(v) (((v) & 0x1) << 15)
+#define BFM_CPM_APCR_LOCK(v) BM_CPM_APCR_LOCK
+#define BF_CPM_APCR_LOCK_V(e) BF_CPM_APCR_LOCK(BV_CPM_APCR_LOCK__##e)
+#define BFM_CPM_APCR_LOCK_V(v) BM_CPM_APCR_LOCK
+#define BP_CPM_APCR_ON 10
+#define BM_CPM_APCR_ON 0x400
+#define BF_CPM_APCR_ON(v) (((v) & 0x1) << 10)
+#define BFM_CPM_APCR_ON(v) BM_CPM_APCR_ON
+#define BF_CPM_APCR_ON_V(e) BF_CPM_APCR_ON(BV_CPM_APCR_ON__##e)
+#define BFM_CPM_APCR_ON_V(v) BM_CPM_APCR_ON
+#define BP_CPM_APCR_BYPASS 9
+#define BM_CPM_APCR_BYPASS 0x200
+#define BF_CPM_APCR_BYPASS(v) (((v) & 0x1) << 9)
+#define BFM_CPM_APCR_BYPASS(v) BM_CPM_APCR_BYPASS
+#define BF_CPM_APCR_BYPASS_V(e) BF_CPM_APCR_BYPASS(BV_CPM_APCR_BYPASS__##e)
+#define BFM_CPM_APCR_BYPASS_V(v) BM_CPM_APCR_BYPASS
+#define BP_CPM_APCR_ENABLE 8
+#define BM_CPM_APCR_ENABLE 0x100
+#define BF_CPM_APCR_ENABLE(v) (((v) & 0x1) << 8)
+#define BFM_CPM_APCR_ENABLE(v) BM_CPM_APCR_ENABLE
+#define BF_CPM_APCR_ENABLE_V(e) BF_CPM_APCR_ENABLE(BV_CPM_APCR_ENABLE__##e)
+#define BFM_CPM_APCR_ENABLE_V(v) BM_CPM_APCR_ENABLE
+
+#define REG_CPM_MPCR jz_reg(CPM_MPCR)
+#define JA_CPM_MPCR (0xb0000000 + 0x14)
+#define JT_CPM_MPCR JIO_32_RW
+#define JN_CPM_MPCR CPM_MPCR
+#define JI_CPM_MPCR
+#define BP_CPM_MPCR_PLLM 24
+#define BM_CPM_MPCR_PLLM 0x7f000000
+#define BF_CPM_MPCR_PLLM(v) (((v) & 0x7f) << 24)
+#define BFM_CPM_MPCR_PLLM(v) BM_CPM_MPCR_PLLM
+#define BF_CPM_MPCR_PLLM_V(e) BF_CPM_MPCR_PLLM(BV_CPM_MPCR_PLLM__##e)
+#define BFM_CPM_MPCR_PLLM_V(v) BM_CPM_MPCR_PLLM
+#define BP_CPM_MPCR_PLLN 18
+#define BM_CPM_MPCR_PLLN 0x7c0000
+#define BF_CPM_MPCR_PLLN(v) (((v) & 0x1f) << 18)
+#define BFM_CPM_MPCR_PLLN(v) BM_CPM_MPCR_PLLN
+#define BF_CPM_MPCR_PLLN_V(e) BF_CPM_MPCR_PLLN(BV_CPM_MPCR_PLLN__##e)
+#define BFM_CPM_MPCR_PLLN_V(v) BM_CPM_MPCR_PLLN
+#define BP_CPM_MPCR_PLLOD 16
+#define BM_CPM_MPCR_PLLOD 0x30000
+#define BF_CPM_MPCR_PLLOD(v) (((v) & 0x3) << 16)
+#define BFM_CPM_MPCR_PLLOD(v) BM_CPM_MPCR_PLLOD
+#define BF_CPM_MPCR_PLLOD_V(e) BF_CPM_MPCR_PLLOD(BV_CPM_MPCR_PLLOD__##e)
+#define BFM_CPM_MPCR_PLLOD_V(v) BM_CPM_MPCR_PLLOD
+#define BP_CPM_MPCR_BS 31
+#define BM_CPM_MPCR_BS 0x80000000
+#define BF_CPM_MPCR_BS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_MPCR_BS(v) BM_CPM_MPCR_BS
+#define BF_CPM_MPCR_BS_V(e) BF_CPM_MPCR_BS(BV_CPM_MPCR_BS__##e)
+#define BFM_CPM_MPCR_BS_V(v) BM_CPM_MPCR_BS
+#define BP_CPM_MPCR_ENABLE 7
+#define BM_CPM_MPCR_ENABLE 0x80
+#define BF_CPM_MPCR_ENABLE(v) (((v) & 0x1) << 7)
+#define BFM_CPM_MPCR_ENABLE(v) BM_CPM_MPCR_ENABLE
+#define BF_CPM_MPCR_ENABLE_V(e) BF_CPM_MPCR_ENABLE(BV_CPM_MPCR_ENABLE__##e)
+#define BFM_CPM_MPCR_ENABLE_V(v) BM_CPM_MPCR_ENABLE
+#define BP_CPM_MPCR_BYPASS 6
+#define BM_CPM_MPCR_BYPASS 0x40
+#define BF_CPM_MPCR_BYPASS(v) (((v) & 0x1) << 6)
+#define BFM_CPM_MPCR_BYPASS(v) BM_CPM_MPCR_BYPASS
+#define BF_CPM_MPCR_BYPASS_V(e) BF_CPM_MPCR_BYPASS(BV_CPM_MPCR_BYPASS__##e)
+#define BFM_CPM_MPCR_BYPASS_V(v) BM_CPM_MPCR_BYPASS
+#define BP_CPM_MPCR_LOCK 1
+#define BM_CPM_MPCR_LOCK 0x2
+#define BF_CPM_MPCR_LOCK(v) (((v) & 0x1) << 1)
+#define BFM_CPM_MPCR_LOCK(v) BM_CPM_MPCR_LOCK
+#define BF_CPM_MPCR_LOCK_V(e) BF_CPM_MPCR_LOCK(BV_CPM_MPCR_LOCK__##e)
+#define BFM_CPM_MPCR_LOCK_V(v) BM_CPM_MPCR_LOCK
+#define BP_CPM_MPCR_ON 0
+#define BM_CPM_MPCR_ON 0x1
+#define BF_CPM_MPCR_ON(v) (((v) & 0x1) << 0)
+#define BFM_CPM_MPCR_ON(v) BM_CPM_MPCR_ON
+#define BF_CPM_MPCR_ON_V(e) BF_CPM_MPCR_ON(BV_CPM_MPCR_ON__##e)
+#define BFM_CPM_MPCR_ON_V(v) BM_CPM_MPCR_ON
+
+#define REG_CPM_LCR jz_reg(CPM_LCR)
+#define JA_CPM_LCR (0xb0000000 + 0x4)
+#define JT_CPM_LCR JIO_32_RW
+#define JN_CPM_LCR CPM_LCR
+#define JI_CPM_LCR
+#define BP_CPM_LCR_PST 8
+#define BM_CPM_LCR_PST 0xfff00
+#define BF_CPM_LCR_PST(v) (((v) & 0xfff) << 8)
+#define BFM_CPM_LCR_PST(v) BM_CPM_LCR_PST
+#define BF_CPM_LCR_PST_V(e) BF_CPM_LCR_PST(BV_CPM_LCR_PST__##e)
+#define BFM_CPM_LCR_PST_V(v) BM_CPM_LCR_PST
+#define BP_CPM_LCR_LPM 0
+#define BM_CPM_LCR_LPM 0x3
+#define BV_CPM_LCR_LPM__IDLE 0x0
+#define BV_CPM_LCR_LPM__SLEEP 0x1
+#define BF_CPM_LCR_LPM(v) (((v) & 0x3) << 0)
+#define BFM_CPM_LCR_LPM(v) BM_CPM_LCR_LPM
+#define BF_CPM_LCR_LPM_V(e) BF_CPM_LCR_LPM(BV_CPM_LCR_LPM__##e)
+#define BFM_CPM_LCR_LPM_V(v) BM_CPM_LCR_LPM
+
+#define REG_CPM_PSWC0ST jz_reg(CPM_PSWC0ST)
+#define JA_CPM_PSWC0ST (0xb0000000 + 0x90)
+#define JT_CPM_PSWC0ST JIO_32_RW
+#define JN_CPM_PSWC0ST CPM_PSWC0ST
+#define JI_CPM_PSWC0ST
+
+#define REG_CPM_PSWC1ST jz_reg(CPM_PSWC1ST)
+#define JA_CPM_PSWC1ST (0xb0000000 + 0x94)
+#define JT_CPM_PSWC1ST JIO_32_RW
+#define JN_CPM_PSWC1ST CPM_PSWC1ST
+#define JI_CPM_PSWC1ST
+
+#define REG_CPM_PSWC2ST jz_reg(CPM_PSWC2ST)
+#define JA_CPM_PSWC2ST (0xb0000000 + 0x98)
+#define JT_CPM_PSWC2ST JIO_32_RW
+#define JN_CPM_PSWC2ST CPM_PSWC2ST
+#define JI_CPM_PSWC2ST
+
+#define REG_CPM_PSWC3ST jz_reg(CPM_PSWC3ST)
+#define JA_CPM_PSWC3ST (0xb0000000 + 0x9c)
+#define JT_CPM_PSWC3ST JIO_32_RW
+#define JN_CPM_PSWC3ST CPM_PSWC3ST
+#define JI_CPM_PSWC3ST
+
+#define REG_CPM_CLKGR jz_reg(CPM_CLKGR)
+#define JA_CPM_CLKGR (0xb0000000 + 0x20)
+#define JT_CPM_CLKGR JIO_32_RW
+#define JN_CPM_CLKGR CPM_CLKGR
+#define JI_CPM_CLKGR
+#define BP_CPM_CLKGR_DDR 31
+#define BM_CPM_CLKGR_DDR 0x80000000
+#define BF_CPM_CLKGR_DDR(v) (((v) & 0x1) << 31)
+#define BFM_CPM_CLKGR_DDR(v) BM_CPM_CLKGR_DDR
+#define BF_CPM_CLKGR_DDR_V(e) BF_CPM_CLKGR_DDR(BV_CPM_CLKGR_DDR__##e)
+#define BFM_CPM_CLKGR_DDR_V(v) BM_CPM_CLKGR_DDR
+#define BP_CPM_CLKGR_CPU_BIT 30
+#define BM_CPM_CLKGR_CPU_BIT 0x40000000
+#define BF_CPM_CLKGR_CPU_BIT(v) (((v) & 0x1) << 30)
+#define BFM_CPM_CLKGR_CPU_BIT(v) BM_CPM_CLKGR_CPU_BIT
+#define BF_CPM_CLKGR_CPU_BIT_V(e) BF_CPM_CLKGR_CPU_BIT(BV_CPM_CLKGR_CPU_BIT__##e)
+#define BFM_CPM_CLKGR_CPU_BIT_V(v) BM_CPM_CLKGR_CPU_BIT
+#define BP_CPM_CLKGR_AHB0 29
+#define BM_CPM_CLKGR_AHB0 0x20000000
+#define BF_CPM_CLKGR_AHB0(v) (((v) & 0x1) << 29)
+#define BFM_CPM_CLKGR_AHB0(v) BM_CPM_CLKGR_AHB0
+#define BF_CPM_CLKGR_AHB0_V(e) BF_CPM_CLKGR_AHB0(BV_CPM_CLKGR_AHB0__##e)
+#define BFM_CPM_CLKGR_AHB0_V(v) BM_CPM_CLKGR_AHB0
+#define BP_CPM_CLKGR_APB0 28
+#define BM_CPM_CLKGR_APB0 0x10000000
+#define BF_CPM_CLKGR_APB0(v) (((v) & 0x1) << 28)
+#define BFM_CPM_CLKGR_APB0(v) BM_CPM_CLKGR_APB0
+#define BF_CPM_CLKGR_APB0_V(e) BF_CPM_CLKGR_APB0(BV_CPM_CLKGR_APB0__##e)
+#define BFM_CPM_CLKGR_APB0_V(v) BM_CPM_CLKGR_APB0
+#define BP_CPM_CLKGR_RTC 27
+#define BM_CPM_CLKGR_RTC 0x8000000
+#define BF_CPM_CLKGR_RTC(v) (((v) & 0x1) << 27)
+#define BFM_CPM_CLKGR_RTC(v) BM_CPM_CLKGR_RTC
+#define BF_CPM_CLKGR_RTC_V(e) BF_CPM_CLKGR_RTC(BV_CPM_CLKGR_RTC__##e)
+#define BFM_CPM_CLKGR_RTC_V(v) BM_CPM_CLKGR_RTC
+#define BP_CPM_CLKGR_PCM 26
+#define BM_CPM_CLKGR_PCM 0x4000000
+#define BF_CPM_CLKGR_PCM(v) (((v) & 0x1) << 26)
+#define BFM_CPM_CLKGR_PCM(v) BM_CPM_CLKGR_PCM
+#define BF_CPM_CLKGR_PCM_V(e) BF_CPM_CLKGR_PCM(BV_CPM_CLKGR_PCM__##e)
+#define BFM_CPM_CLKGR_PCM_V(v) BM_CPM_CLKGR_PCM
+#define BP_CPM_CLKGR_MAC 25
+#define BM_CPM_CLKGR_MAC 0x2000000
+#define BF_CPM_CLKGR_MAC(v) (((v) & 0x1) << 25)
+#define BFM_CPM_CLKGR_MAC(v) BM_CPM_CLKGR_MAC
+#define BF_CPM_CLKGR_MAC_V(e) BF_CPM_CLKGR_MAC(BV_CPM_CLKGR_MAC__##e)
+#define BFM_CPM_CLKGR_MAC_V(v) BM_CPM_CLKGR_MAC
+#define BP_CPM_CLKGR_AES 24
+#define BM_CPM_CLKGR_AES 0x1000000
+#define BF_CPM_CLKGR_AES(v) (((v) & 0x1) << 24)
+#define BFM_CPM_CLKGR_AES(v) BM_CPM_CLKGR_AES
+#define BF_CPM_CLKGR_AES_V(e) BF_CPM_CLKGR_AES(BV_CPM_CLKGR_AES__##e)
+#define BFM_CPM_CLKGR_AES_V(v) BM_CPM_CLKGR_AES
+#define BP_CPM_CLKGR_LCD 23
+#define BM_CPM_CLKGR_LCD 0x800000
+#define BF_CPM_CLKGR_LCD(v) (((v) & 0x1) << 23)
+#define BFM_CPM_CLKGR_LCD(v) BM_CPM_CLKGR_LCD
+#define BF_CPM_CLKGR_LCD_V(e) BF_CPM_CLKGR_LCD(BV_CPM_CLKGR_LCD__##e)
+#define BFM_CPM_CLKGR_LCD_V(v) BM_CPM_CLKGR_LCD
+#define BP_CPM_CLKGR_CIM 22
+#define BM_CPM_CLKGR_CIM 0x400000
+#define BF_CPM_CLKGR_CIM(v) (((v) & 0x1) << 22)
+#define BFM_CPM_CLKGR_CIM(v) BM_CPM_CLKGR_CIM
+#define BF_CPM_CLKGR_CIM_V(e) BF_CPM_CLKGR_CIM(BV_CPM_CLKGR_CIM__##e)
+#define BFM_CPM_CLKGR_CIM_V(v) BM_CPM_CLKGR_CIM
+#define BP_CPM_CLKGR_PDMA 21
+#define BM_CPM_CLKGR_PDMA 0x200000
+#define BF_CPM_CLKGR_PDMA(v) (((v) & 0x1) << 21)
+#define BFM_CPM_CLKGR_PDMA(v) BM_CPM_CLKGR_PDMA
+#define BF_CPM_CLKGR_PDMA_V(e) BF_CPM_CLKGR_PDMA(BV_CPM_CLKGR_PDMA__##e)
+#define BFM_CPM_CLKGR_PDMA_V(v) BM_CPM_CLKGR_PDMA
+#define BP_CPM_CLKGR_OST 20
+#define BM_CPM_CLKGR_OST 0x100000
+#define BF_CPM_CLKGR_OST(v) (((v) & 0x1) << 20)
+#define BFM_CPM_CLKGR_OST(v) BM_CPM_CLKGR_OST
+#define BF_CPM_CLKGR_OST_V(e) BF_CPM_CLKGR_OST(BV_CPM_CLKGR_OST__##e)
+#define BFM_CPM_CLKGR_OST_V(v) BM_CPM_CLKGR_OST
+#define BP_CPM_CLKGR_SSI 19
+#define BM_CPM_CLKGR_SSI 0x80000
+#define BF_CPM_CLKGR_SSI(v) (((v) & 0x1) << 19)
+#define BFM_CPM_CLKGR_SSI(v) BM_CPM_CLKGR_SSI
+#define BF_CPM_CLKGR_SSI_V(e) BF_CPM_CLKGR_SSI(BV_CPM_CLKGR_SSI__##e)
+#define BFM_CPM_CLKGR_SSI_V(v) BM_CPM_CLKGR_SSI
+#define BP_CPM_CLKGR_TCU 18
+#define BM_CPM_CLKGR_TCU 0x40000
+#define BF_CPM_CLKGR_TCU(v) (((v) & 0x1) << 18)
+#define BFM_CPM_CLKGR_TCU(v) BM_CPM_CLKGR_TCU
+#define BF_CPM_CLKGR_TCU_V(e) BF_CPM_CLKGR_TCU(BV_CPM_CLKGR_TCU__##e)
+#define BFM_CPM_CLKGR_TCU_V(v) BM_CPM_CLKGR_TCU
+#define BP_CPM_CLKGR_DMIC 17
+#define BM_CPM_CLKGR_DMIC 0x20000
+#define BF_CPM_CLKGR_DMIC(v) (((v) & 0x1) << 17)
+#define BFM_CPM_CLKGR_DMIC(v) BM_CPM_CLKGR_DMIC
+#define BF_CPM_CLKGR_DMIC_V(e) BF_CPM_CLKGR_DMIC(BV_CPM_CLKGR_DMIC__##e)
+#define BFM_CPM_CLKGR_DMIC_V(v) BM_CPM_CLKGR_DMIC
+#define BP_CPM_CLKGR_UART2 16
+#define BM_CPM_CLKGR_UART2 0x10000
+#define BF_CPM_CLKGR_UART2(v) (((v) & 0x1) << 16)
+#define BFM_CPM_CLKGR_UART2(v) BM_CPM_CLKGR_UART2
+#define BF_CPM_CLKGR_UART2_V(e) BF_CPM_CLKGR_UART2(BV_CPM_CLKGR_UART2__##e)
+#define BFM_CPM_CLKGR_UART2_V(v) BM_CPM_CLKGR_UART2
+#define BP_CPM_CLKGR_UART1 15
+#define BM_CPM_CLKGR_UART1 0x8000
+#define BF_CPM_CLKGR_UART1(v) (((v) & 0x1) << 15)
+#define BFM_CPM_CLKGR_UART1(v) BM_CPM_CLKGR_UART1
+#define BF_CPM_CLKGR_UART1_V(e) BF_CPM_CLKGR_UART1(BV_CPM_CLKGR_UART1__##e)
+#define BFM_CPM_CLKGR_UART1_V(v) BM_CPM_CLKGR_UART1
+#define BP_CPM_CLKGR_UART0 14
+#define BM_CPM_CLKGR_UART0 0x4000
+#define BF_CPM_CLKGR_UART0(v) (((v) & 0x1) << 14)
+#define BFM_CPM_CLKGR_UART0(v) BM_CPM_CLKGR_UART0
+#define BF_CPM_CLKGR_UART0_V(e) BF_CPM_CLKGR_UART0(BV_CPM_CLKGR_UART0__##e)
+#define BFM_CPM_CLKGR_UART0_V(v) BM_CPM_CLKGR_UART0
+#define BP_CPM_CLKGR_JPEG 12
+#define BM_CPM_CLKGR_JPEG 0x1000
+#define BF_CPM_CLKGR_JPEG(v) (((v) & 0x1) << 12)
+#define BFM_CPM_CLKGR_JPEG(v) BM_CPM_CLKGR_JPEG
+#define BF_CPM_CLKGR_JPEG_V(e) BF_CPM_CLKGR_JPEG(BV_CPM_CLKGR_JPEG__##e)
+#define BFM_CPM_CLKGR_JPEG_V(v) BM_CPM_CLKGR_JPEG
+#define BP_CPM_CLKGR_AIC 11
+#define BM_CPM_CLKGR_AIC 0x800
+#define BF_CPM_CLKGR_AIC(v) (((v) & 0x1) << 11)
+#define BFM_CPM_CLKGR_AIC(v) BM_CPM_CLKGR_AIC
+#define BF_CPM_CLKGR_AIC_V(e) BF_CPM_CLKGR_AIC(BV_CPM_CLKGR_AIC__##e)
+#define BFM_CPM_CLKGR_AIC_V(v) BM_CPM_CLKGR_AIC
+#define BP_CPM_CLKGR_I2C2 9
+#define BM_CPM_CLKGR_I2C2 0x200
+#define BF_CPM_CLKGR_I2C2(v) (((v) & 0x1) << 9)
+#define BFM_CPM_CLKGR_I2C2(v) BM_CPM_CLKGR_I2C2
+#define BF_CPM_CLKGR_I2C2_V(e) BF_CPM_CLKGR_I2C2(BV_CPM_CLKGR_I2C2__##e)
+#define BFM_CPM_CLKGR_I2C2_V(v) BM_CPM_CLKGR_I2C2
+#define BP_CPM_CLKGR_I2C1 8
+#define BM_CPM_CLKGR_I2C1 0x100
+#define BF_CPM_CLKGR_I2C1(v) (((v) & 0x1) << 8)
+#define BFM_CPM_CLKGR_I2C1(v) BM_CPM_CLKGR_I2C1
+#define BF_CPM_CLKGR_I2C1_V(e) BF_CPM_CLKGR_I2C1(BV_CPM_CLKGR_I2C1__##e)
+#define BFM_CPM_CLKGR_I2C1_V(v) BM_CPM_CLKGR_I2C1
+#define BP_CPM_CLKGR_I2C0 7
+#define BM_CPM_CLKGR_I2C0 0x80
+#define BF_CPM_CLKGR_I2C0(v) (((v) & 0x1) << 7)
+#define BFM_CPM_CLKGR_I2C0(v) BM_CPM_CLKGR_I2C0
+#define BF_CPM_CLKGR_I2C0_V(e) BF_CPM_CLKGR_I2C0(BV_CPM_CLKGR_I2C0__##e)
+#define BFM_CPM_CLKGR_I2C0_V(v) BM_CPM_CLKGR_I2C0
+#define BP_CPM_CLKGR_SCC 6
+#define BM_CPM_CLKGR_SCC 0x40
+#define BF_CPM_CLKGR_SCC(v) (((v) & 0x1) << 6)
+#define BFM_CPM_CLKGR_SCC(v) BM_CPM_CLKGR_SCC
+#define BF_CPM_CLKGR_SCC_V(e) BF_CPM_CLKGR_SCC(BV_CPM_CLKGR_SCC__##e)
+#define BFM_CPM_CLKGR_SCC_V(v) BM_CPM_CLKGR_SCC
+#define BP_CPM_CLKGR_MSC1 5
+#define BM_CPM_CLKGR_MSC1 0x20
+#define BF_CPM_CLKGR_MSC1(v) (((v) & 0x1) << 5)
+#define BFM_CPM_CLKGR_MSC1(v) BM_CPM_CLKGR_MSC1
+#define BF_CPM_CLKGR_MSC1_V(e) BF_CPM_CLKGR_MSC1(BV_CPM_CLKGR_MSC1__##e)
+#define BFM_CPM_CLKGR_MSC1_V(v) BM_CPM_CLKGR_MSC1
+#define BP_CPM_CLKGR_MSC0 4
+#define BM_CPM_CLKGR_MSC0 0x10
+#define BF_CPM_CLKGR_MSC0(v) (((v) & 0x1) << 4)
+#define BFM_CPM_CLKGR_MSC0(v) BM_CPM_CLKGR_MSC0
+#define BF_CPM_CLKGR_MSC0_V(e) BF_CPM_CLKGR_MSC0(BV_CPM_CLKGR_MSC0__##e)
+#define BFM_CPM_CLKGR_MSC0_V(v) BM_CPM_CLKGR_MSC0
+#define BP_CPM_CLKGR_OTG 3
+#define BM_CPM_CLKGR_OTG 0x8
+#define BF_CPM_CLKGR_OTG(v) (((v) & 0x1) << 3)
+#define BFM_CPM_CLKGR_OTG(v) BM_CPM_CLKGR_OTG
+#define BF_CPM_CLKGR_OTG_V(e) BF_CPM_CLKGR_OTG(BV_CPM_CLKGR_OTG__##e)
+#define BFM_CPM_CLKGR_OTG_V(v) BM_CPM_CLKGR_OTG
+#define BP_CPM_CLKGR_SFC 2
+#define BM_CPM_CLKGR_SFC 0x4
+#define BF_CPM_CLKGR_SFC(v) (((v) & 0x1) << 2)
+#define BFM_CPM_CLKGR_SFC(v) BM_CPM_CLKGR_SFC
+#define BF_CPM_CLKGR_SFC_V(e) BF_CPM_CLKGR_SFC(BV_CPM_CLKGR_SFC__##e)
+#define BFM_CPM_CLKGR_SFC_V(v) BM_CPM_CLKGR_SFC
+#define BP_CPM_CLKGR_EFUSE 1
+#define BM_CPM_CLKGR_EFUSE 0x2
+#define BF_CPM_CLKGR_EFUSE(v) (((v) & 0x1) << 1)
+#define BFM_CPM_CLKGR_EFUSE(v) BM_CPM_CLKGR_EFUSE
+#define BF_CPM_CLKGR_EFUSE_V(e) BF_CPM_CLKGR_EFUSE(BV_CPM_CLKGR_EFUSE__##e)
+#define BFM_CPM_CLKGR_EFUSE_V(v) BM_CPM_CLKGR_EFUSE
+
+#define REG_CPM_OPCR jz_reg(CPM_OPCR)
+#define JA_CPM_OPCR (0xb0000000 + 0x24)
+#define JT_CPM_OPCR JIO_32_RW
+#define JN_CPM_OPCR CPM_OPCR
+#define JI_CPM_OPCR
+#define BP_CPM_OPCR_O1ST 8
+#define BM_CPM_OPCR_O1ST 0xfff00
+#define BF_CPM_OPCR_O1ST(v) (((v) & 0xfff) << 8)
+#define BFM_CPM_OPCR_O1ST(v) BM_CPM_OPCR_O1ST
+#define BF_CPM_OPCR_O1ST_V(e) BF_CPM_OPCR_O1ST(BV_CPM_OPCR_O1ST__##e)
+#define BFM_CPM_OPCR_O1ST_V(v) BM_CPM_OPCR_O1ST
+#define BP_CPM_OPCR_IDLE_DIS 31
+#define BM_CPM_OPCR_IDLE_DIS 0x80000000
+#define BF_CPM_OPCR_IDLE_DIS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_OPCR_IDLE_DIS(v) BM_CPM_OPCR_IDLE_DIS
+#define BF_CPM_OPCR_IDLE_DIS_V(e) BF_CPM_OPCR_IDLE_DIS(BV_CPM_OPCR_IDLE_DIS__##e)
+#define BFM_CPM_OPCR_IDLE_DIS_V(v) BM_CPM_OPCR_IDLE_DIS
+#define BP_CPM_OPCR_MASK_INT 30
+#define BM_CPM_OPCR_MASK_INT 0x40000000
+#define BF_CPM_OPCR_MASK_INT(v) (((v) & 0x1) << 30)
+#define BFM_CPM_OPCR_MASK_INT(v) BM_CPM_OPCR_MASK_INT
+#define BF_CPM_OPCR_MASK_INT_V(e) BF_CPM_OPCR_MASK_INT(BV_CPM_OPCR_MASK_INT__##e)
+#define BFM_CPM_OPCR_MASK_INT_V(v) BM_CPM_OPCR_MASK_INT
+#define BP_CPM_OPCR_MASK_VPU 29
+#define BM_CPM_OPCR_MASK_VPU 0x20000000
+#define BF_CPM_OPCR_MASK_VPU(v) (((v) & 0x1) << 29)
+#define BFM_CPM_OPCR_MASK_VPU(v) BM_CPM_OPCR_MASK_VPU
+#define BF_CPM_OPCR_MASK_VPU_V(e) BF_CPM_OPCR_MASK_VPU(BV_CPM_OPCR_MASK_VPU__##e)
+#define BFM_CPM_OPCR_MASK_VPU_V(v) BM_CPM_OPCR_MASK_VPU
+#define BP_CPM_OPCR_GATE_SCLK_A_BUS 28
+#define BM_CPM_OPCR_GATE_SCLK_A_BUS 0x10000000
+#define BF_CPM_OPCR_GATE_SCLK_A_BUS(v) (((v) & 0x1) << 28)
+#define BFM_CPM_OPCR_GATE_SCLK_A_BUS(v) BM_CPM_OPCR_GATE_SCLK_A_BUS
+#define BF_CPM_OPCR_GATE_SCLK_A_BUS_V(e) BF_CPM_OPCR_GATE_SCLK_A_BUS(BV_CPM_OPCR_GATE_SCLK_A_BUS__##e)
+#define BFM_CPM_OPCR_GATE_SCLK_A_BUS_V(v) BM_CPM_OPCR_GATE_SCLK_A_BUS
+#define BP_CPM_OPCR_L2C_PD 25
+#define BM_CPM_OPCR_L2C_PD 0x2000000
+#define BF_CPM_OPCR_L2C_PD(v) (((v) & 0x1) << 25)
+#define BFM_CPM_OPCR_L2C_PD(v) BM_CPM_OPCR_L2C_PD
+#define BF_CPM_OPCR_L2C_PD_V(e) BF_CPM_OPCR_L2C_PD(BV_CPM_OPCR_L2C_PD__##e)
+#define BFM_CPM_OPCR_L2C_PD_V(v) BM_CPM_OPCR_L2C_PD
+#define BP_CPM_OPCR_REQ_MODE 24
+#define BM_CPM_OPCR_REQ_MODE 0x1000000
+#define BF_CPM_OPCR_REQ_MODE(v) (((v) & 0x1) << 24)
+#define BFM_CPM_OPCR_REQ_MODE(v) BM_CPM_OPCR_REQ_MODE
+#define BF_CPM_OPCR_REQ_MODE_V(e) BF_CPM_OPCR_REQ_MODE(BV_CPM_OPCR_REQ_MODE__##e)
+#define BFM_CPM_OPCR_REQ_MODE_V(v) BM_CPM_OPCR_REQ_MODE
+#define BP_CPM_OPCR_GATE_USBPHY_CLK 23
+#define BM_CPM_OPCR_GATE_USBPHY_CLK 0x800000
+#define BF_CPM_OPCR_GATE_USBPHY_CLK(v) (((v) & 0x1) << 23)
+#define BFM_CPM_OPCR_GATE_USBPHY_CLK(v) BM_CPM_OPCR_GATE_USBPHY_CLK
+#define BF_CPM_OPCR_GATE_USBPHY_CLK_V(e) BF_CPM_OPCR_GATE_USBPHY_CLK(BV_CPM_OPCR_GATE_USBPHY_CLK__##e)
+#define BFM_CPM_OPCR_GATE_USBPHY_CLK_V(v) BM_CPM_OPCR_GATE_USBPHY_CLK
+#define BP_CPM_OPCR_DIS_STOP_MUX 22
+#define BM_CPM_OPCR_DIS_STOP_MUX 0x400000
+#define BF_CPM_OPCR_DIS_STOP_MUX(v) (((v) & 0x1) << 22)
+#define BFM_CPM_OPCR_DIS_STOP_MUX(v) BM_CPM_OPCR_DIS_STOP_MUX
+#define BF_CPM_OPCR_DIS_STOP_MUX_V(e) BF_CPM_OPCR_DIS_STOP_MUX(BV_CPM_OPCR_DIS_STOP_MUX__##e)
+#define BFM_CPM_OPCR_DIS_STOP_MUX_V(v) BM_CPM_OPCR_DIS_STOP_MUX
+#define BP_CPM_OPCR_SPENDN0 7
+#define BM_CPM_OPCR_SPENDN0 0x80
+#define BF_CPM_OPCR_SPENDN0(v) (((v) & 0x1) << 7)
+#define BFM_CPM_OPCR_SPENDN0(v) BM_CPM_OPCR_SPENDN0
+#define BF_CPM_OPCR_SPENDN0_V(e) BF_CPM_OPCR_SPENDN0(BV_CPM_OPCR_SPENDN0__##e)
+#define BFM_CPM_OPCR_SPENDN0_V(v) BM_CPM_OPCR_SPENDN0
+#define BP_CPM_OPCR_SPENDN1 6
+#define BM_CPM_OPCR_SPENDN1 0x40
+#define BF_CPM_OPCR_SPENDN1(v) (((v) & 0x1) << 6)
+#define BFM_CPM_OPCR_SPENDN1(v) BM_CPM_OPCR_SPENDN1
+#define BF_CPM_OPCR_SPENDN1_V(e) BF_CPM_OPCR_SPENDN1(BV_CPM_OPCR_SPENDN1__##e)
+#define BFM_CPM_OPCR_SPENDN1_V(v) BM_CPM_OPCR_SPENDN1
+#define BP_CPM_OPCR_CPU_MODE 5
+#define BM_CPM_OPCR_CPU_MODE 0x20
+#define BF_CPM_OPCR_CPU_MODE(v) (((v) & 0x1) << 5)
+#define BFM_CPM_OPCR_CPU_MODE(v) BM_CPM_OPCR_CPU_MODE
+#define BF_CPM_OPCR_CPU_MODE_V(e) BF_CPM_OPCR_CPU_MODE(BV_CPM_OPCR_CPU_MODE__##e)
+#define BFM_CPM_OPCR_CPU_MODE_V(v) BM_CPM_OPCR_CPU_MODE
+#define BP_CPM_OPCR_O1SE 4
+#define BM_CPM_OPCR_O1SE 0x10
+#define BF_CPM_OPCR_O1SE(v) (((v) & 0x1) << 4)
+#define BFM_CPM_OPCR_O1SE(v) BM_CPM_OPCR_O1SE
+#define BF_CPM_OPCR_O1SE_V(e) BF_CPM_OPCR_O1SE(BV_CPM_OPCR_O1SE__##e)
+#define BFM_CPM_OPCR_O1SE_V(v) BM_CPM_OPCR_O1SE
+#define BP_CPM_OPCR_PD 3
+#define BM_CPM_OPCR_PD 0x8
+#define BF_CPM_OPCR_PD(v) (((v) & 0x1) << 3)
+#define BFM_CPM_OPCR_PD(v) BM_CPM_OPCR_PD
+#define BF_CPM_OPCR_PD_V(e) BF_CPM_OPCR_PD(BV_CPM_OPCR_PD__##e)
+#define BFM_CPM_OPCR_PD_V(v) BM_CPM_OPCR_PD
+#define BP_CPM_OPCR_ERCS 2
+#define BM_CPM_OPCR_ERCS 0x4
+#define BF_CPM_OPCR_ERCS(v) (((v) & 0x1) << 2)
+#define BFM_CPM_OPCR_ERCS(v) BM_CPM_OPCR_ERCS
+#define BF_CPM_OPCR_ERCS_V(e) BF_CPM_OPCR_ERCS(BV_CPM_OPCR_ERCS__##e)
+#define BFM_CPM_OPCR_ERCS_V(v) BM_CPM_OPCR_ERCS
+#define BP_CPM_OPCR_BUS_MODE 1
+#define BM_CPM_OPCR_BUS_MODE 0x2
+#define BF_CPM_OPCR_BUS_MODE(v) (((v) & 0x1) << 1)
+#define BFM_CPM_OPCR_BUS_MODE(v) BM_CPM_OPCR_BUS_MODE
+#define BF_CPM_OPCR_BUS_MODE_V(e) BF_CPM_OPCR_BUS_MODE(BV_CPM_OPCR_BUS_MODE__##e)
+#define BFM_CPM_OPCR_BUS_MODE_V(v) BM_CPM_OPCR_BUS_MODE
+
+#endif /* __HEADERGEN_CPM_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ddrc.h b/firmware/target/mips/ingenic_x1000/x1000/ddrc.h
new file mode 100644
index 0000000000..f482969a4e
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/ddrc.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_DDRC_H__
+#define __HEADERGEN_DDRC_H__
+
+#include "macro.h"
+
+#define REG_DDRC_STATUS jz_reg(DDRC_STATUS)
+#define JA_DDRC_STATUS (0xb34f0000 + 0x0)
+#define JT_DDRC_STATUS JIO_32_RW
+#define JN_DDRC_STATUS DDRC_STATUS
+#define JI_DDRC_STATUS
+
+#define REG_DDRC_CFG jz_reg(DDRC_CFG)
+#define JA_DDRC_CFG (0xb34f0000 + 0x4)
+#define JT_DDRC_CFG JIO_32_RW
+#define JN_DDRC_CFG DDRC_CFG
+#define JI_DDRC_CFG
+
+#define REG_DDRC_CTRL jz_reg(DDRC_CTRL)
+#define JA_DDRC_CTRL (0xb34f0000 + 0x8)
+#define JT_DDRC_CTRL JIO_32_RW
+#define JN_DDRC_CTRL DDRC_CTRL
+#define JI_DDRC_CTRL
+
+#define REG_DDRC_TIMING1 jz_reg(DDRC_TIMING1)
+#define JA_DDRC_TIMING1 (0xb34f0000 + 0x60)
+#define JT_DDRC_TIMING1 JIO_32_RW
+#define JN_DDRC_TIMING1 DDRC_TIMING1
+#define JI_DDRC_TIMING1
+
+#define REG_DDRC_TIMING2 jz_reg(DDRC_TIMING2)
+#define JA_DDRC_TIMING2 (0xb34f0000 + 0x64)
+#define JT_DDRC_TIMING2 JIO_32_RW
+#define JN_DDRC_TIMING2 DDRC_TIMING2
+#define JI_DDRC_TIMING2
+
+#define REG_DDRC_TIMING3 jz_reg(DDRC_TIMING3)
+#define JA_DDRC_TIMING3 (0xb34f0000 + 0x68)
+#define JT_DDRC_TIMING3 JIO_32_RW
+#define JN_DDRC_TIMING3 DDRC_TIMING3
+#define JI_DDRC_TIMING3
+
+#define REG_DDRC_TIMING4 jz_reg(DDRC_TIMING4)
+#define JA_DDRC_TIMING4 (0xb34f0000 + 0x6c)
+#define JT_DDRC_TIMING4 JIO_32_RW
+#define JN_DDRC_TIMING4 DDRC_TIMING4
+#define JI_DDRC_TIMING4
+
+#define REG_DDRC_TIMING5 jz_reg(DDRC_TIMING5)
+#define JA_DDRC_TIMING5 (0xb34f0000 + 0x70)
+#define JT_DDRC_TIMING5 JIO_32_RW
+#define JN_DDRC_TIMING5 DDRC_TIMING5
+#define JI_DDRC_TIMING5
+
+#define REG_DDRC_TIMING6 jz_reg(DDRC_TIMING6)
+#define JA_DDRC_TIMING6 (0xb34f0000 + 0x74)
+#define JT_DDRC_TIMING6 JIO_32_RW
+#define JN_DDRC_TIMING6 DDRC_TIMING6
+#define JI_DDRC_TIMING6
+
+#define REG_DDRC_REFCNT jz_reg(DDRC_REFCNT)
+#define JA_DDRC_REFCNT (0xb34f0000 + 0x18)
+#define JT_DDRC_REFCNT JIO_32_RW
+#define JN_DDRC_REFCNT DDRC_REFCNT
+#define JI_DDRC_REFCNT
+
+#define REG_DDRC_MMAP0 jz_reg(DDRC_MMAP0)
+#define JA_DDRC_MMAP0 (0xb34f0000 + 0x24)
+#define JT_DDRC_MMAP0 JIO_32_RW
+#define JN_DDRC_MMAP0 DDRC_MMAP0
+#define JI_DDRC_MMAP0
+
+#define REG_DDRC_MMAP1 jz_reg(DDRC_MMAP1)
+#define JA_DDRC_MMAP1 (0xb34f0000 + 0x28)
+#define JT_DDRC_MMAP1 JIO_32_RW
+#define JN_DDRC_MMAP1 DDRC_MMAP1
+#define JI_DDRC_MMAP1
+
+#define REG_DDRC_DLP jz_reg(DDRC_DLP)
+#define JA_DDRC_DLP (0xb34f0000 + 0xbc)
+#define JT_DDRC_DLP JIO_32_RW
+#define JN_DDRC_DLP DDRC_DLP
+#define JI_DDRC_DLP
+
+#define REG_DDRC_REMAP1 jz_reg(DDRC_REMAP1)
+#define JA_DDRC_REMAP1 (0xb34f0000 + 0x9c)
+#define JT_DDRC_REMAP1 JIO_32_RW
+#define JN_DDRC_REMAP1 DDRC_REMAP1
+#define JI_DDRC_REMAP1
+
+#define REG_DDRC_REMAP2 jz_reg(DDRC_REMAP2)
+#define JA_DDRC_REMAP2 (0xb34f0000 + 0xa0)
+#define JT_DDRC_REMAP2 JIO_32_RW
+#define JN_DDRC_REMAP2 DDRC_REMAP2
+#define JI_DDRC_REMAP2
+
+#define REG_DDRC_REMAP3 jz_reg(DDRC_REMAP3)
+#define JA_DDRC_REMAP3 (0xb34f0000 + 0xa4)
+#define JT_DDRC_REMAP3 JIO_32_RW
+#define JN_DDRC_REMAP3 DDRC_REMAP3
+#define JI_DDRC_REMAP3
+
+#define REG_DDRC_REMAP4 jz_reg(DDRC_REMAP4)
+#define JA_DDRC_REMAP4 (0xb34f0000 + 0xa8)
+#define JT_DDRC_REMAP4 JIO_32_RW
+#define JN_DDRC_REMAP4 DDRC_REMAP4
+#define JI_DDRC_REMAP4
+
+#define REG_DDRC_REMAP5 jz_reg(DDRC_REMAP5)
+#define JA_DDRC_REMAP5 (0xb34f0000 + 0xac)
+#define JT_DDRC_REMAP5 JIO_32_RW
+#define JN_DDRC_REMAP5 DDRC_REMAP5
+#define JI_DDRC_REMAP5
+
+#define REG_DDRC_AUTOSR_CNT jz_reg(DDRC_AUTOSR_CNT)
+#define JA_DDRC_AUTOSR_CNT (0xb34f0000 + 0x308)
+#define JT_DDRC_AUTOSR_CNT JIO_32_RW
+#define JN_DDRC_AUTOSR_CNT DDRC_AUTOSR_CNT
+#define JI_DDRC_AUTOSR_CNT
+
+#define REG_DDRC_AUTOSR_EN jz_reg(DDRC_AUTOSR_EN)
+#define JA_DDRC_AUTOSR_EN (0xb34f0000 + 0x304)
+#define JT_DDRC_AUTOSR_EN JIO_32_RW
+#define JN_DDRC_AUTOSR_EN DDRC_AUTOSR_EN
+#define JI_DDRC_AUTOSR_EN
+
+#endif /* __HEADERGEN_DDRC_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ddrc_apb.h b/firmware/target/mips/ingenic_x1000/x1000/ddrc_apb.h
new file mode 100644
index 0000000000..bcb880624f
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/ddrc_apb.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_DDRC_APB_H__
+#define __HEADERGEN_DDRC_APB_H__
+
+#include "macro.h"
+
+#define REG_DDRC_APB_CLKSTP_CFG jz_reg(DDRC_APB_CLKSTP_CFG)
+#define JA_DDRC_APB_CLKSTP_CFG (0xb3012000 + 0x68)
+#define JT_DDRC_APB_CLKSTP_CFG JIO_32_RW
+#define JN_DDRC_APB_CLKSTP_CFG DDRC_APB_CLKSTP_CFG
+#define JI_DDRC_APB_CLKSTP_CFG
+
+#define REG_DDRC_APB_PHYRST_CFG jz_reg(DDRC_APB_PHYRST_CFG)
+#define JA_DDRC_APB_PHYRST_CFG (0xb3012000 + 0x80)
+#define JT_DDRC_APB_PHYRST_CFG JIO_32_RW
+#define JN_DDRC_APB_PHYRST_CFG DDRC_APB_PHYRST_CFG
+#define JI_DDRC_APB_PHYRST_CFG
+
+#endif /* __HEADERGEN_DDRC_APB_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ddrphy.h b/firmware/target/mips/ingenic_x1000/x1000/ddrphy.h
new file mode 100644
index 0000000000..2ac0563090
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/ddrphy.h
@@ -0,0 +1,155 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_DDRPHY_H__
+#define __HEADERGEN_DDRPHY_H__
+
+#include "macro.h"
+
+#define REG_DDRPHY_PIR jz_reg(DDRPHY_PIR)
+#define JA_DDRPHY_PIR (0xb3011000 + 0x4)
+#define JT_DDRPHY_PIR JIO_32_RW
+#define JN_DDRPHY_PIR DDRPHY_PIR
+#define JI_DDRPHY_PIR
+
+#define REG_DDRPHY_PGCR jz_reg(DDRPHY_PGCR)
+#define JA_DDRPHY_PGCR (0xb3011000 + 0x8)
+#define JT_DDRPHY_PGCR JIO_32_RW
+#define JN_DDRPHY_PGCR DDRPHY_PGCR
+#define JI_DDRPHY_PGCR
+
+#define REG_DDRPHY_PGSR jz_reg(DDRPHY_PGSR)
+#define JA_DDRPHY_PGSR (0xb3011000 + 0xc)
+#define JT_DDRPHY_PGSR JIO_32_RW
+#define JN_DDRPHY_PGSR DDRPHY_PGSR
+#define JI_DDRPHY_PGSR
+
+#define REG_DDRPHY_DLLGCR jz_reg(DDRPHY_DLLGCR)
+#define JA_DDRPHY_DLLGCR (0xb3011000 + 0x10)
+#define JT_DDRPHY_DLLGCR JIO_32_RW
+#define JN_DDRPHY_DLLGCR DDRPHY_DLLGCR
+#define JI_DDRPHY_DLLGCR
+
+#define REG_DDRPHY_ACDLLCR jz_reg(DDRPHY_ACDLLCR)
+#define JA_DDRPHY_ACDLLCR (0xb3011000 + 0x14)
+#define JT_DDRPHY_ACDLLCR JIO_32_RW
+#define JN_DDRPHY_ACDLLCR DDRPHY_ACDLLCR
+#define JI_DDRPHY_ACDLLCR
+
+#define REG_DDRPHY_PTR0 jz_reg(DDRPHY_PTR0)
+#define JA_DDRPHY_PTR0 (0xb3011000 + 0x18)
+#define JT_DDRPHY_PTR0 JIO_32_RW
+#define JN_DDRPHY_PTR0 DDRPHY_PTR0
+#define JI_DDRPHY_PTR0
+
+#define REG_DDRPHY_PTR1 jz_reg(DDRPHY_PTR1)
+#define JA_DDRPHY_PTR1 (0xb3011000 + 0x1c)
+#define JT_DDRPHY_PTR1 JIO_32_RW
+#define JN_DDRPHY_PTR1 DDRPHY_PTR1
+#define JI_DDRPHY_PTR1
+
+#define REG_DDRPHY_PTR2 jz_reg(DDRPHY_PTR2)
+#define JA_DDRPHY_PTR2 (0xb3011000 + 0x20)
+#define JT_DDRPHY_PTR2 JIO_32_RW
+#define JN_DDRPHY_PTR2 DDRPHY_PTR2
+#define JI_DDRPHY_PTR2
+
+#define REG_DDRPHY_ACIOCR jz_reg(DDRPHY_ACIOCR)
+#define JA_DDRPHY_ACIOCR (0xb3011000 + 0x24)
+#define JT_DDRPHY_ACIOCR JIO_32_RW
+#define JN_DDRPHY_ACIOCR DDRPHY_ACIOCR
+#define JI_DDRPHY_ACIOCR
+
+#define REG_DDRPHY_DXCCR jz_reg(DDRPHY_DXCCR)
+#define JA_DDRPHY_DXCCR (0xb3011000 + 0x28)
+#define JT_DDRPHY_DXCCR JIO_32_RW
+#define JN_DDRPHY_DXCCR DDRPHY_DXCCR
+#define JI_DDRPHY_DXCCR
+
+#define REG_DDRPHY_DSGCR jz_reg(DDRPHY_DSGCR)
+#define JA_DDRPHY_DSGCR (0xb3011000 + 0x2c)
+#define JT_DDRPHY_DSGCR JIO_32_RW
+#define JN_DDRPHY_DSGCR DDRPHY_DSGCR
+#define JI_DDRPHY_DSGCR
+
+#define REG_DDRPHY_DCR jz_reg(DDRPHY_DCR)
+#define JA_DDRPHY_DCR (0xb3011000 + 0x30)
+#define JT_DDRPHY_DCR JIO_32_RW
+#define JN_DDRPHY_DCR DDRPHY_DCR
+#define JI_DDRPHY_DCR
+
+#define REG_DDRPHY_DTPR0 jz_reg(DDRPHY_DTPR0)
+#define JA_DDRPHY_DTPR0 (0xb3011000 + 0x34)
+#define JT_DDRPHY_DTPR0 JIO_32_RW
+#define JN_DDRPHY_DTPR0 DDRPHY_DTPR0
+#define JI_DDRPHY_DTPR0
+
+#define REG_DDRPHY_DTPR1 jz_reg(DDRPHY_DTPR1)
+#define JA_DDRPHY_DTPR1 (0xb3011000 + 0x38)
+#define JT_DDRPHY_DTPR1 JIO_32_RW
+#define JN_DDRPHY_DTPR1 DDRPHY_DTPR1
+#define JI_DDRPHY_DTPR1
+
+#define REG_DDRPHY_DTPR2 jz_reg(DDRPHY_DTPR2)
+#define JA_DDRPHY_DTPR2 (0xb3011000 + 0x3c)
+#define JT_DDRPHY_DTPR2 JIO_32_RW
+#define JN_DDRPHY_DTPR2 DDRPHY_DTPR2
+#define JI_DDRPHY_DTPR2
+
+#define REG_DDRPHY_MR0 jz_reg(DDRPHY_MR0)
+#define JA_DDRPHY_MR0 (0xb3011000 + 0x40)
+#define JT_DDRPHY_MR0 JIO_32_RW
+#define JN_DDRPHY_MR0 DDRPHY_MR0
+#define JI_DDRPHY_MR0
+
+#define REG_DDRPHY_MR1 jz_reg(DDRPHY_MR1)
+#define JA_DDRPHY_MR1 (0xb3011000 + 0x44)
+#define JT_DDRPHY_MR1 JIO_32_RW
+#define JN_DDRPHY_MR1 DDRPHY_MR1
+#define JI_DDRPHY_MR1
+
+#define REG_DDRPHY_MR2 jz_reg(DDRPHY_MR2)
+#define JA_DDRPHY_MR2 (0xb3011000 + 0x48)
+#define JT_DDRPHY_MR2 JIO_32_RW
+#define JN_DDRPHY_MR2 DDRPHY_MR2
+#define JI_DDRPHY_MR2
+
+#define REG_DDRPHY_MR3 jz_reg(DDRPHY_MR3)
+#define JA_DDRPHY_MR3 (0xb3011000 + 0x4c)
+#define JT_DDRPHY_MR3 JIO_32_RW
+#define JN_DDRPHY_MR3 DDRPHY_MR3
+#define JI_DDRPHY_MR3
+
+#define REG_DDRPHY_DTAR jz_reg(DDRPHY_DTAR)
+#define JA_DDRPHY_DTAR (0xb3011000 + 0x54)
+#define JT_DDRPHY_DTAR JIO_32_RW
+#define JN_DDRPHY_DTAR DDRPHY_DTAR
+#define JI_DDRPHY_DTAR
+
+#define REG_DDRPHY_DXGCR(_n1) jz_reg(DDRPHY_DXGCR(_n1))
+#define JA_DDRPHY_DXGCR(_n1) (0xb3011000 + 0x1c0 + (_n1) * 0x40)
+#define JT_DDRPHY_DXGCR(_n1) JIO_32_RW
+#define JN_DDRPHY_DXGCR(_n1) DDRPHY_DXGCR
+#define JI_DDRPHY_DXGCR(_n1) (_n1)
+
+#endif /* __HEADERGEN_DDRPHY_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/dma.h b/firmware/target/mips/ingenic_x1000/x1000/dma.h
new file mode 100644
index 0000000000..516c6e6849
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/dma.h
@@ -0,0 +1,112 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_DMA_H__
+#define __HEADERGEN_DMA_H__
+
+#include "macro.h"
+
+#define REG_DMA_CTRL jz_reg(DMA_CTRL)
+#define JA_DMA_CTRL (0xb3421000 + 0x0)
+#define JT_DMA_CTRL JIO_32_RW
+#define JN_DMA_CTRL DMA_CTRL
+#define JI_DMA_CTRL
+#define BP_DMA_CTRL_FMSC 31
+#define BM_DMA_CTRL_FMSC 0x80000000
+#define BF_DMA_CTRL_FMSC(v) (((v) & 0x1) << 31)
+#define BFM_DMA_CTRL_FMSC(v) BM_DMA_CTRL_FMSC
+#define BF_DMA_CTRL_FMSC_V(e) BF_DMA_CTRL_FMSC(BV_DMA_CTRL_FMSC__##e)
+#define BFM_DMA_CTRL_FMSC_V(v) BM_DMA_CTRL_FMSC
+#define BP_DMA_CTRL_FSSI 30
+#define BM_DMA_CTRL_FSSI 0x40000000
+#define BF_DMA_CTRL_FSSI(v) (((v) & 0x1) << 30)
+#define BFM_DMA_CTRL_FSSI(v) BM_DMA_CTRL_FSSI
+#define BF_DMA_CTRL_FSSI_V(e) BF_DMA_CTRL_FSSI(BV_DMA_CTRL_FSSI__##e)
+#define BFM_DMA_CTRL_FSSI_V(v) BM_DMA_CTRL_FSSI
+#define BP_DMA_CTRL_FTSSI 29
+#define BM_DMA_CTRL_FTSSI 0x20000000
+#define BF_DMA_CTRL_FTSSI(v) (((v) & 0x1) << 29)
+#define BFM_DMA_CTRL_FTSSI(v) BM_DMA_CTRL_FTSSI
+#define BF_DMA_CTRL_FTSSI_V(e) BF_DMA_CTRL_FTSSI(BV_DMA_CTRL_FTSSI__##e)
+#define BFM_DMA_CTRL_FTSSI_V(v) BM_DMA_CTRL_FTSSI
+#define BP_DMA_CTRL_FUART 28
+#define BM_DMA_CTRL_FUART 0x10000000
+#define BF_DMA_CTRL_FUART(v) (((v) & 0x1) << 28)
+#define BFM_DMA_CTRL_FUART(v) BM_DMA_CTRL_FUART
+#define BF_DMA_CTRL_FUART_V(e) BF_DMA_CTRL_FUART(BV_DMA_CTRL_FUART__##e)
+#define BFM_DMA_CTRL_FUART_V(v) BM_DMA_CTRL_FUART
+#define BP_DMA_CTRL_FAIC 27
+#define BM_DMA_CTRL_FAIC 0x8000000
+#define BF_DMA_CTRL_FAIC(v) (((v) & 0x1) << 27)
+#define BFM_DMA_CTRL_FAIC(v) BM_DMA_CTRL_FAIC
+#define BF_DMA_CTRL_FAIC_V(e) BF_DMA_CTRL_FAIC(BV_DMA_CTRL_FAIC__##e)
+#define BFM_DMA_CTRL_FAIC_V(v) BM_DMA_CTRL_FAIC
+#define BP_DMA_CTRL_HALT 3
+#define BM_DMA_CTRL_HALT 0x8
+#define BF_DMA_CTRL_HALT(v) (((v) & 0x1) << 3)
+#define BFM_DMA_CTRL_HALT(v) BM_DMA_CTRL_HALT
+#define BF_DMA_CTRL_HALT_V(e) BF_DMA_CTRL_HALT(BV_DMA_CTRL_HALT__##e)
+#define BFM_DMA_CTRL_HALT_V(v) BM_DMA_CTRL_HALT
+#define BP_DMA_CTRL_AR 2
+#define BM_DMA_CTRL_AR 0x4
+#define BF_DMA_CTRL_AR(v) (((v) & 0x1) << 2)
+#define BFM_DMA_CTRL_AR(v) BM_DMA_CTRL_AR
+#define BF_DMA_CTRL_AR_V(e) BF_DMA_CTRL_AR(BV_DMA_CTRL_AR__##e)
+#define BFM_DMA_CTRL_AR_V(v) BM_DMA_CTRL_AR
+#define BP_DMA_CTRL_ENABLE 0
+#define BM_DMA_CTRL_ENABLE 0x1
+#define BF_DMA_CTRL_ENABLE(v) (((v) & 0x1) << 0)
+#define BFM_DMA_CTRL_ENABLE(v) BM_DMA_CTRL_ENABLE
+#define BF_DMA_CTRL_ENABLE_V(e) BF_DMA_CTRL_ENABLE(BV_DMA_CTRL_ENABLE__##e)
+#define BFM_DMA_CTRL_ENABLE_V(v) BM_DMA_CTRL_ENABLE
+
+#define REG_DMA_IRQP jz_reg(DMA_IRQP)
+#define JA_DMA_IRQP (0xb3421000 + 0x4)
+#define JT_DMA_IRQP JIO_32_RW
+#define JN_DMA_IRQP DMA_IRQP
+#define JI_DMA_IRQP
+
+#define REG_DMA_DB jz_reg(DMA_DB)
+#define JA_DMA_DB (0xb3421000 + 0x8)
+#define JT_DMA_DB JIO_32_RW
+#define JN_DMA_DB DMA_DB
+#define JI_DMA_DB
+#define REG_DMA_DB_SET jz_reg(DMA_DB_SET)
+#define JA_DMA_DB_SET (JA_DMA_DB + 0x4)
+#define JT_DMA_DB_SET JIO_32_WO
+#define JN_DMA_DB_SET DMA_DB
+#define JI_DMA_DB_SET
+
+#define REG_DMA_DIP jz_reg(DMA_DIP)
+#define JA_DMA_DIP (0xb3421000 + 0x10)
+#define JT_DMA_DIP JIO_32_RW
+#define JN_DMA_DIP DMA_DIP
+#define JI_DMA_DIP
+
+#define REG_DMA_DIC jz_reg(DMA_DIC)
+#define JA_DMA_DIC (0xb3421000 + 0x14)
+#define JT_DMA_DIC JIO_32_RW
+#define JN_DMA_DIC DMA_DIC
+#define JI_DMA_DIC
+
+#endif /* __HEADERGEN_DMA_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/dma_chn.h b/firmware/target/mips/ingenic_x1000/x1000/dma_chn.h
new file mode 100644
index 0000000000..56eb2a8cc1
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/dma_chn.h
@@ -0,0 +1,253 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_DMA_CHN_H__
+#define __HEADERGEN_DMA_CHN_H__
+
+#include "macro.h"
+
+#define REG_DMA_CHN_SA(_n1) jz_reg(DMA_CHN_SA(_n1))
+#define JA_DMA_CHN_SA(_n1) (0xb3420000 + (_n1) * 0x20 + 0x0)
+#define JT_DMA_CHN_SA(_n1) JIO_32_RW
+#define JN_DMA_CHN_SA(_n1) DMA_CHN_SA
+#define JI_DMA_CHN_SA(_n1) (_n1)
+
+#define REG_DMA_CHN_TA(_n1) jz_reg(DMA_CHN_TA(_n1))
+#define JA_DMA_CHN_TA(_n1) (0xb3420000 + (_n1) * 0x20 + 0x4)
+#define JT_DMA_CHN_TA(_n1) JIO_32_RW
+#define JN_DMA_CHN_TA(_n1) DMA_CHN_TA
+#define JI_DMA_CHN_TA(_n1) (_n1)
+
+#define REG_DMA_CHN_TC(_n1) jz_reg(DMA_CHN_TC(_n1))
+#define JA_DMA_CHN_TC(_n1) (0xb3420000 + (_n1) * 0x20 + 0x8)
+#define JT_DMA_CHN_TC(_n1) JIO_32_RW
+#define JN_DMA_CHN_TC(_n1) DMA_CHN_TC
+#define JI_DMA_CHN_TC(_n1) (_n1)
+#define BP_DMA_CHN_TC_DOA 24
+#define BM_DMA_CHN_TC_DOA 0xff000000
+#define BF_DMA_CHN_TC_DOA(v) (((v) & 0xff) << 24)
+#define BFM_DMA_CHN_TC_DOA(v) BM_DMA_CHN_TC_DOA
+#define BF_DMA_CHN_TC_DOA_V(e) BF_DMA_CHN_TC_DOA(BV_DMA_CHN_TC_DOA__##e)
+#define BFM_DMA_CHN_TC_DOA_V(v) BM_DMA_CHN_TC_DOA
+#define BP_DMA_CHN_TC_CNT 0
+#define BM_DMA_CHN_TC_CNT 0xffffff
+#define BF_DMA_CHN_TC_CNT(v) (((v) & 0xffffff) << 0)
+#define BFM_DMA_CHN_TC_CNT(v) BM_DMA_CHN_TC_CNT
+#define BF_DMA_CHN_TC_CNT_V(e) BF_DMA_CHN_TC_CNT(BV_DMA_CHN_TC_CNT__##e)
+#define BFM_DMA_CHN_TC_CNT_V(v) BM_DMA_CHN_TC_CNT
+
+#define REG_DMA_CHN_RT(_n1) jz_reg(DMA_CHN_RT(_n1))
+#define JA_DMA_CHN_RT(_n1) (0xb3420000 + (_n1) * 0x20 + 0xc)
+#define JT_DMA_CHN_RT(_n1) JIO_32_RW
+#define JN_DMA_CHN_RT(_n1) DMA_CHN_RT
+#define JI_DMA_CHN_RT(_n1) (_n1)
+#define BP_DMA_CHN_RT_TYPE 0
+#define BM_DMA_CHN_RT_TYPE 0x3f
+#define BV_DMA_CHN_RT_TYPE__DMIC_RX 0x5
+#define BV_DMA_CHN_RT_TYPE__I2S_TX 0x6
+#define BV_DMA_CHN_RT_TYPE__I2S_RX 0x7
+#define BV_DMA_CHN_RT_TYPE__AUTO 0x8
+#define BV_DMA_CHN_RT_TYPE__UART2_TX 0x10
+#define BV_DMA_CHN_RT_TYPE__UART2_RX 0x11
+#define BV_DMA_CHN_RT_TYPE__UART1_TX 0x12
+#define BV_DMA_CHN_RT_TYPE__UART1_RX 0x13
+#define BV_DMA_CHN_RT_TYPE__UART0_TX 0x14
+#define BV_DMA_CHN_RT_TYPE__UART0_RX 0x15
+#define BV_DMA_CHN_RT_TYPE__SSI_TX 0x16
+#define BV_DMA_CHN_RT_TYPE__SSI_RX 0x17
+#define BV_DMA_CHN_RT_TYPE__MSC0_TX 0x1a
+#define BV_DMA_CHN_RT_TYPE__MSC0_RX 0x1b
+#define BV_DMA_CHN_RT_TYPE__MSC1_TX 0x1c
+#define BV_DMA_CHN_RT_TYPE__MSC1_RX 0x1d
+#define BV_DMA_CHN_RT_TYPE__PCM_TX 0x20
+#define BV_DMA_CHN_RT_TYPE__PCM_RX 0x21
+#define BV_DMA_CHN_RT_TYPE__I2C0_TX 0x24
+#define BV_DMA_CHN_RT_TYPE__I2C0_RX 0x25
+#define BV_DMA_CHN_RT_TYPE__I2C1_TX 0x26
+#define BV_DMA_CHN_RT_TYPE__I2C1_RX 0x27
+#define BV_DMA_CHN_RT_TYPE__I2C2_TX 0x28
+#define BV_DMA_CHN_RT_TYPE__I2C2_RX 0x29
+#define BF_DMA_CHN_RT_TYPE(v) (((v) & 0x3f) << 0)
+#define BFM_DMA_CHN_RT_TYPE(v) BM_DMA_CHN_RT_TYPE
+#define BF_DMA_CHN_RT_TYPE_V(e) BF_DMA_CHN_RT_TYPE(BV_DMA_CHN_RT_TYPE__##e)
+#define BFM_DMA_CHN_RT_TYPE_V(v) BM_DMA_CHN_RT_TYPE
+
+#define REG_DMA_CHN_CS(_n1) jz_reg(DMA_CHN_CS(_n1))
+#define JA_DMA_CHN_CS(_n1) (0xb3420000 + (_n1) * 0x20 + 0x10)
+#define JT_DMA_CHN_CS(_n1) JIO_32_RW
+#define JN_DMA_CHN_CS(_n1) DMA_CHN_CS
+#define JI_DMA_CHN_CS(_n1) (_n1)
+#define BP_DMA_CHN_CS_CDOA 8
+#define BM_DMA_CHN_CS_CDOA 0xff00
+#define BF_DMA_CHN_CS_CDOA(v) (((v) & 0xff) << 8)
+#define BFM_DMA_CHN_CS_CDOA(v) BM_DMA_CHN_CS_CDOA
+#define BF_DMA_CHN_CS_CDOA_V(e) BF_DMA_CHN_CS_CDOA(BV_DMA_CHN_CS_CDOA__##e)
+#define BFM_DMA_CHN_CS_CDOA_V(v) BM_DMA_CHN_CS_CDOA
+#define BP_DMA_CHN_CS_NDES 31
+#define BM_DMA_CHN_CS_NDES 0x80000000
+#define BF_DMA_CHN_CS_NDES(v) (((v) & 0x1) << 31)
+#define BFM_DMA_CHN_CS_NDES(v) BM_DMA_CHN_CS_NDES
+#define BF_DMA_CHN_CS_NDES_V(e) BF_DMA_CHN_CS_NDES(BV_DMA_CHN_CS_NDES__##e)
+#define BFM_DMA_CHN_CS_NDES_V(v) BM_DMA_CHN_CS_NDES
+#define BP_DMA_CHN_CS_DES8 30
+#define BM_DMA_CHN_CS_DES8 0x40000000
+#define BF_DMA_CHN_CS_DES8(v) (((v) & 0x1) << 30)
+#define BFM_DMA_CHN_CS_DES8(v) BM_DMA_CHN_CS_DES8
+#define BF_DMA_CHN_CS_DES8_V(e) BF_DMA_CHN_CS_DES8(BV_DMA_CHN_CS_DES8__##e)
+#define BFM_DMA_CHN_CS_DES8_V(v) BM_DMA_CHN_CS_DES8
+#define BP_DMA_CHN_CS_AR 4
+#define BM_DMA_CHN_CS_AR 0x10
+#define BF_DMA_CHN_CS_AR(v) (((v) & 0x1) << 4)
+#define BFM_DMA_CHN_CS_AR(v) BM_DMA_CHN_CS_AR
+#define BF_DMA_CHN_CS_AR_V(e) BF_DMA_CHN_CS_AR(BV_DMA_CHN_CS_AR__##e)
+#define BFM_DMA_CHN_CS_AR_V(v) BM_DMA_CHN_CS_AR
+#define BP_DMA_CHN_CS_TT 3
+#define BM_DMA_CHN_CS_TT 0x8
+#define BF_DMA_CHN_CS_TT(v) (((v) & 0x1) << 3)
+#define BFM_DMA_CHN_CS_TT(v) BM_DMA_CHN_CS_TT
+#define BF_DMA_CHN_CS_TT_V(e) BF_DMA_CHN_CS_TT(BV_DMA_CHN_CS_TT__##e)
+#define BFM_DMA_CHN_CS_TT_V(v) BM_DMA_CHN_CS_TT
+#define BP_DMA_CHN_CS_HLT 2
+#define BM_DMA_CHN_CS_HLT 0x4
+#define BF_DMA_CHN_CS_HLT(v) (((v) & 0x1) << 2)
+#define BFM_DMA_CHN_CS_HLT(v) BM_DMA_CHN_CS_HLT
+#define BF_DMA_CHN_CS_HLT_V(e) BF_DMA_CHN_CS_HLT(BV_DMA_CHN_CS_HLT__##e)
+#define BFM_DMA_CHN_CS_HLT_V(v) BM_DMA_CHN_CS_HLT
+#define BP_DMA_CHN_CS_CTE 0
+#define BM_DMA_CHN_CS_CTE 0x1
+#define BF_DMA_CHN_CS_CTE(v) (((v) & 0x1) << 0)
+#define BFM_DMA_CHN_CS_CTE(v) BM_DMA_CHN_CS_CTE
+#define BF_DMA_CHN_CS_CTE_V(e) BF_DMA_CHN_CS_CTE(BV_DMA_CHN_CS_CTE__##e)
+#define BFM_DMA_CHN_CS_CTE_V(v) BM_DMA_CHN_CS_CTE
+
+#define REG_DMA_CHN_CM(_n1) jz_reg(DMA_CHN_CM(_n1))
+#define JA_DMA_CHN_CM(_n1) (0xb3420000 + (_n1) * 0x20 + 0x14)
+#define JT_DMA_CHN_CM(_n1) JIO_32_RW
+#define JN_DMA_CHN_CM(_n1) DMA_CHN_CM
+#define JI_DMA_CHN_CM(_n1) (_n1)
+#define BP_DMA_CHN_CM_RDIL 16
+#define BM_DMA_CHN_CM_RDIL 0xf0000
+#define BF_DMA_CHN_CM_RDIL(v) (((v) & 0xf) << 16)
+#define BFM_DMA_CHN_CM_RDIL(v) BM_DMA_CHN_CM_RDIL
+#define BF_DMA_CHN_CM_RDIL_V(e) BF_DMA_CHN_CM_RDIL(BV_DMA_CHN_CM_RDIL__##e)
+#define BFM_DMA_CHN_CM_RDIL_V(v) BM_DMA_CHN_CM_RDIL
+#define BP_DMA_CHN_CM_SP 14
+#define BM_DMA_CHN_CM_SP 0xc000
+#define BV_DMA_CHN_CM_SP__32BIT 0x0
+#define BV_DMA_CHN_CM_SP__8BIT 0x1
+#define BV_DMA_CHN_CM_SP__16BIT 0x2
+#define BF_DMA_CHN_CM_SP(v) (((v) & 0x3) << 14)
+#define BFM_DMA_CHN_CM_SP(v) BM_DMA_CHN_CM_SP
+#define BF_DMA_CHN_CM_SP_V(e) BF_DMA_CHN_CM_SP(BV_DMA_CHN_CM_SP__##e)
+#define BFM_DMA_CHN_CM_SP_V(v) BM_DMA_CHN_CM_SP
+#define BP_DMA_CHN_CM_DP 12
+#define BM_DMA_CHN_CM_DP 0x3000
+#define BV_DMA_CHN_CM_DP__32BIT 0x0
+#define BV_DMA_CHN_CM_DP__8BIT 0x1
+#define BV_DMA_CHN_CM_DP__16BIT 0x2
+#define BF_DMA_CHN_CM_DP(v) (((v) & 0x3) << 12)
+#define BFM_DMA_CHN_CM_DP(v) BM_DMA_CHN_CM_DP
+#define BF_DMA_CHN_CM_DP_V(e) BF_DMA_CHN_CM_DP(BV_DMA_CHN_CM_DP__##e)
+#define BFM_DMA_CHN_CM_DP_V(v) BM_DMA_CHN_CM_DP
+#define BP_DMA_CHN_CM_TSZ 8
+#define BM_DMA_CHN_CM_TSZ 0x700
+#define BV_DMA_CHN_CM_TSZ__32BIT 0x0
+#define BV_DMA_CHN_CM_TSZ__8BIT 0x1
+#define BV_DMA_CHN_CM_TSZ__16BIT 0x2
+#define BV_DMA_CHN_CM_TSZ__16BYTE 0x3
+#define BV_DMA_CHN_CM_TSZ__32BYTE 0x4
+#define BV_DMA_CHN_CM_TSZ__64BYTE 0x5
+#define BV_DMA_CHN_CM_TSZ__128BYTE 0x6
+#define BV_DMA_CHN_CM_TSZ__AUTO 0x7
+#define BF_DMA_CHN_CM_TSZ(v) (((v) & 0x7) << 8)
+#define BFM_DMA_CHN_CM_TSZ(v) BM_DMA_CHN_CM_TSZ
+#define BF_DMA_CHN_CM_TSZ_V(e) BF_DMA_CHN_CM_TSZ(BV_DMA_CHN_CM_TSZ__##e)
+#define BFM_DMA_CHN_CM_TSZ_V(v) BM_DMA_CHN_CM_TSZ
+#define BP_DMA_CHN_CM_SAI 23
+#define BM_DMA_CHN_CM_SAI 0x800000
+#define BF_DMA_CHN_CM_SAI(v) (((v) & 0x1) << 23)
+#define BFM_DMA_CHN_CM_SAI(v) BM_DMA_CHN_CM_SAI
+#define BF_DMA_CHN_CM_SAI_V(e) BF_DMA_CHN_CM_SAI(BV_DMA_CHN_CM_SAI__##e)
+#define BFM_DMA_CHN_CM_SAI_V(v) BM_DMA_CHN_CM_SAI
+#define BP_DMA_CHN_CM_DAI 22
+#define BM_DMA_CHN_CM_DAI 0x400000
+#define BF_DMA_CHN_CM_DAI(v) (((v) & 0x1) << 22)
+#define BFM_DMA_CHN_CM_DAI(v) BM_DMA_CHN_CM_DAI
+#define BF_DMA_CHN_CM_DAI_V(e) BF_DMA_CHN_CM_DAI(BV_DMA_CHN_CM_DAI__##e)
+#define BFM_DMA_CHN_CM_DAI_V(v) BM_DMA_CHN_CM_DAI
+#define BP_DMA_CHN_CM_STDE 2
+#define BM_DMA_CHN_CM_STDE 0x4
+#define BF_DMA_CHN_CM_STDE(v) (((v) & 0x1) << 2)
+#define BFM_DMA_CHN_CM_STDE(v) BM_DMA_CHN_CM_STDE
+#define BF_DMA_CHN_CM_STDE_V(e) BF_DMA_CHN_CM_STDE(BV_DMA_CHN_CM_STDE__##e)
+#define BFM_DMA_CHN_CM_STDE_V(v) BM_DMA_CHN_CM_STDE
+#define BP_DMA_CHN_CM_TIE 1
+#define BM_DMA_CHN_CM_TIE 0x2
+#define BF_DMA_CHN_CM_TIE(v) (((v) & 0x1) << 1)
+#define BFM_DMA_CHN_CM_TIE(v) BM_DMA_CHN_CM_TIE
+#define BF_DMA_CHN_CM_TIE_V(e) BF_DMA_CHN_CM_TIE(BV_DMA_CHN_CM_TIE__##e)
+#define BFM_DMA_CHN_CM_TIE_V(v) BM_DMA_CHN_CM_TIE
+#define BP_DMA_CHN_CM_LINK 0
+#define BM_DMA_CHN_CM_LINK 0x1
+#define BF_DMA_CHN_CM_LINK(v) (((v) & 0x1) << 0)
+#define BFM_DMA_CHN_CM_LINK(v) BM_DMA_CHN_CM_LINK
+#define BF_DMA_CHN_CM_LINK_V(e) BF_DMA_CHN_CM_LINK(BV_DMA_CHN_CM_LINK__##e)
+#define BFM_DMA_CHN_CM_LINK_V(v) BM_DMA_CHN_CM_LINK
+
+#define REG_DMA_CHN_DA(_n1) jz_reg(DMA_CHN_DA(_n1))
+#define JA_DMA_CHN_DA(_n1) (0xb3420000 + (_n1) * 0x20 + 0x18)
+#define JT_DMA_CHN_DA(_n1) JIO_32_RW
+#define JN_DMA_CHN_DA(_n1) DMA_CHN_DA
+#define JI_DMA_CHN_DA(_n1) (_n1)
+#define BP_DMA_CHN_DA_DBA 12
+#define BM_DMA_CHN_DA_DBA 0xfffff000
+#define BF_DMA_CHN_DA_DBA(v) (((v) & 0xfffff) << 12)
+#define BFM_DMA_CHN_DA_DBA(v) BM_DMA_CHN_DA_DBA
+#define BF_DMA_CHN_DA_DBA_V(e) BF_DMA_CHN_DA_DBA(BV_DMA_CHN_DA_DBA__##e)
+#define BFM_DMA_CHN_DA_DBA_V(v) BM_DMA_CHN_DA_DBA
+#define BP_DMA_CHN_DA_DOA 4
+#define BM_DMA_CHN_DA_DOA 0xff0
+#define BF_DMA_CHN_DA_DOA(v) (((v) & 0xff) << 4)
+#define BFM_DMA_CHN_DA_DOA(v) BM_DMA_CHN_DA_DOA
+#define BF_DMA_CHN_DA_DOA_V(e) BF_DMA_CHN_DA_DOA(BV_DMA_CHN_DA_DOA__##e)
+#define BFM_DMA_CHN_DA_DOA_V(v) BM_DMA_CHN_DA_DOA
+
+#define REG_DMA_CHN_SD(_n1) jz_reg(DMA_CHN_SD(_n1))
+#define JA_DMA_CHN_SD(_n1) (0xb3420000 + (_n1) * 0x20 + 0x1c)
+#define JT_DMA_CHN_SD(_n1) JIO_32_RW
+#define JN_DMA_CHN_SD(_n1) DMA_CHN_SD
+#define JI_DMA_CHN_SD(_n1) (_n1)
+#define BP_DMA_CHN_SD_TSD 16
+#define BM_DMA_CHN_SD_TSD 0xffff0000
+#define BF_DMA_CHN_SD_TSD(v) (((v) & 0xffff) << 16)
+#define BFM_DMA_CHN_SD_TSD(v) BM_DMA_CHN_SD_TSD
+#define BF_DMA_CHN_SD_TSD_V(e) BF_DMA_CHN_SD_TSD(BV_DMA_CHN_SD_TSD__##e)
+#define BFM_DMA_CHN_SD_TSD_V(v) BM_DMA_CHN_SD_TSD
+#define BP_DMA_CHN_SD_SSD 0
+#define BM_DMA_CHN_SD_SSD 0xffff
+#define BF_DMA_CHN_SD_SSD(v) (((v) & 0xffff) << 0)
+#define BFM_DMA_CHN_SD_SSD(v) BM_DMA_CHN_SD_SSD
+#define BF_DMA_CHN_SD_SSD_V(e) BF_DMA_CHN_SD_SSD(BV_DMA_CHN_SD_SSD__##e)
+#define BFM_DMA_CHN_SD_SSD_V(v) BM_DMA_CHN_SD_SSD
+
+#endif /* __HEADERGEN_DMA_CHN_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/gpio.h b/firmware/target/mips/ingenic_x1000/x1000/gpio.h
new file mode 100644
index 0000000000..6bba343cf6
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/gpio.h
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_GPIO_H__
+#define __HEADERGEN_GPIO_H__
+
+#include "macro.h"
+
+#define REG_GPIO_C_GLITCH_CFG0 jz_reg(GPIO_C_GLITCH_CFG0)
+#define JA_GPIO_C_GLITCH_CFG0 (0xb0010000 + 0x200 + 0x800)
+#define JT_GPIO_C_GLITCH_CFG0 JIO_32_RW
+#define JN_GPIO_C_GLITCH_CFG0 GPIO_C_GLITCH_CFG0
+#define JI_GPIO_C_GLITCH_CFG0
+#define REG_GPIO_C_GLITCH_CFG0_SET jz_reg(GPIO_C_GLITCH_CFG0_SET)
+#define JA_GPIO_C_GLITCH_CFG0_SET (JA_GPIO_C_GLITCH_CFG0 + 0x4)
+#define JT_GPIO_C_GLITCH_CFG0_SET JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG0_SET GPIO_C_GLITCH_CFG0
+#define JI_GPIO_C_GLITCH_CFG0_SET
+#define REG_GPIO_C_GLITCH_CFG0_CLR jz_reg(GPIO_C_GLITCH_CFG0_CLR)
+#define JA_GPIO_C_GLITCH_CFG0_CLR (JA_GPIO_C_GLITCH_CFG0 + 0x8)
+#define JT_GPIO_C_GLITCH_CFG0_CLR JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG0_CLR GPIO_C_GLITCH_CFG0
+#define JI_GPIO_C_GLITCH_CFG0_CLR
+
+#define REG_GPIO_C_GLITCH_CFG1 jz_reg(GPIO_C_GLITCH_CFG1)
+#define JA_GPIO_C_GLITCH_CFG1 (0xb0010000 + 0x200 + 0x810)
+#define JT_GPIO_C_GLITCH_CFG1 JIO_32_RW
+#define JN_GPIO_C_GLITCH_CFG1 GPIO_C_GLITCH_CFG1
+#define JI_GPIO_C_GLITCH_CFG1
+#define REG_GPIO_C_GLITCH_CFG1_SET jz_reg(GPIO_C_GLITCH_CFG1_SET)
+#define JA_GPIO_C_GLITCH_CFG1_SET (JA_GPIO_C_GLITCH_CFG1 + 0x4)
+#define JT_GPIO_C_GLITCH_CFG1_SET JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG1_SET GPIO_C_GLITCH_CFG1
+#define JI_GPIO_C_GLITCH_CFG1_SET
+#define REG_GPIO_C_GLITCH_CFG1_CLR jz_reg(GPIO_C_GLITCH_CFG1_CLR)
+#define JA_GPIO_C_GLITCH_CFG1_CLR (JA_GPIO_C_GLITCH_CFG1 + 0x8)
+#define JT_GPIO_C_GLITCH_CFG1_CLR JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG1_CLR GPIO_C_GLITCH_CFG1
+#define JI_GPIO_C_GLITCH_CFG1_CLR
+
+#define REG_GPIO_C_GLITCH_CFG2 jz_reg(GPIO_C_GLITCH_CFG2)
+#define JA_GPIO_C_GLITCH_CFG2 (0xb0010000 + 0x200 + 0x820)
+#define JT_GPIO_C_GLITCH_CFG2 JIO_32_RW
+#define JN_GPIO_C_GLITCH_CFG2 GPIO_C_GLITCH_CFG2
+#define JI_GPIO_C_GLITCH_CFG2
+#define REG_GPIO_C_GLITCH_CFG2_SET jz_reg(GPIO_C_GLITCH_CFG2_SET)
+#define JA_GPIO_C_GLITCH_CFG2_SET (JA_GPIO_C_GLITCH_CFG2 + 0x4)
+#define JT_GPIO_C_GLITCH_CFG2_SET JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG2_SET GPIO_C_GLITCH_CFG2
+#define JI_GPIO_C_GLITCH_CFG2_SET
+#define REG_GPIO_C_GLITCH_CFG2_CLR jz_reg(GPIO_C_GLITCH_CFG2_CLR)
+#define JA_GPIO_C_GLITCH_CFG2_CLR (JA_GPIO_C_GLITCH_CFG2 + 0x8)
+#define JT_GPIO_C_GLITCH_CFG2_CLR JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG2_CLR GPIO_C_GLITCH_CFG2
+#define JI_GPIO_C_GLITCH_CFG2_CLR
+
+#define REG_GPIO_C_GLITCH_CFG3 jz_reg(GPIO_C_GLITCH_CFG3)
+#define JA_GPIO_C_GLITCH_CFG3 (0xb0010000 + 0x200 + 0x830)
+#define JT_GPIO_C_GLITCH_CFG3 JIO_32_RW
+#define JN_GPIO_C_GLITCH_CFG3 GPIO_C_GLITCH_CFG3
+#define JI_GPIO_C_GLITCH_CFG3
+#define REG_GPIO_C_GLITCH_CFG3_SET jz_reg(GPIO_C_GLITCH_CFG3_SET)
+#define JA_GPIO_C_GLITCH_CFG3_SET (JA_GPIO_C_GLITCH_CFG3 + 0x4)
+#define JT_GPIO_C_GLITCH_CFG3_SET JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG3_SET GPIO_C_GLITCH_CFG3
+#define JI_GPIO_C_GLITCH_CFG3_SET
+#define REG_GPIO_C_GLITCH_CFG3_CLR jz_reg(GPIO_C_GLITCH_CFG3_CLR)
+#define JA_GPIO_C_GLITCH_CFG3_CLR (JA_GPIO_C_GLITCH_CFG3 + 0x8)
+#define JT_GPIO_C_GLITCH_CFG3_CLR JIO_32_WO
+#define JN_GPIO_C_GLITCH_CFG3_CLR GPIO_C_GLITCH_CFG3
+#define JI_GPIO_C_GLITCH_CFG3_CLR
+
+#define REG_GPIO_PIN(_n1) jz_reg(GPIO_PIN(_n1))
+#define JA_GPIO_PIN(_n1) (0xb0010000 + 0x0 + (_n1) * 0x100)
+#define JT_GPIO_PIN(_n1) JIO_32_RW
+#define JN_GPIO_PIN(_n1) GPIO_PIN
+#define JI_GPIO_PIN(_n1) (_n1)
+
+#define REG_GPIO_INT(_n1) jz_reg(GPIO_INT(_n1))
+#define JA_GPIO_INT(_n1) (0xb0010000 + 0x10 + (_n1) * 0x100)
+#define JT_GPIO_INT(_n1) JIO_32_RW
+#define JN_GPIO_INT(_n1) GPIO_INT
+#define JI_GPIO_INT(_n1) (_n1)
+#define REG_GPIO_INT_SET(_n1) jz_reg(GPIO_INT_SET(_n1))
+#define JA_GPIO_INT_SET(_n1) (JA_GPIO_INT(_n1) + 0x4)
+#define JT_GPIO_INT_SET(_n1) JIO_32_WO
+#define JN_GPIO_INT_SET(_n1) GPIO_INT
+#define JI_GPIO_INT_SET(_n1) (_n1)
+#define REG_GPIO_INT_CLR(_n1) jz_reg(GPIO_INT_CLR(_n1))
+#define JA_GPIO_INT_CLR(_n1) (JA_GPIO_INT(_n1) + 0x8)
+#define JT_GPIO_INT_CLR(_n1) JIO_32_WO
+#define JN_GPIO_INT_CLR(_n1) GPIO_INT
+#define JI_GPIO_INT_CLR(_n1) (_n1)
+
+#define REG_GPIO_MSK(_n1) jz_reg(GPIO_MSK(_n1))
+#define JA_GPIO_MSK(_n1) (0xb0010000 + 0x20 + (_n1) * 0x100)
+#define JT_GPIO_MSK(_n1) JIO_32_RW
+#define JN_GPIO_MSK(_n1) GPIO_MSK
+#define JI_GPIO_MSK(_n1) (_n1)
+#define REG_GPIO_MSK_SET(_n1) jz_reg(GPIO_MSK_SET(_n1))
+#define JA_GPIO_MSK_SET(_n1) (JA_GPIO_MSK(_n1) + 0x4)
+#define JT_GPIO_MSK_SET(_n1) JIO_32_WO
+#define JN_GPIO_MSK_SET(_n1) GPIO_MSK
+#define JI_GPIO_MSK_SET(_n1) (_n1)
+#define REG_GPIO_MSK_CLR(_n1) jz_reg(GPIO_MSK_CLR(_n1))
+#define JA_GPIO_MSK_CLR(_n1) (JA_GPIO_MSK(_n1) + 0x8)
+#define JT_GPIO_MSK_CLR(_n1) JIO_32_WO
+#define JN_GPIO_MSK_CLR(_n1) GPIO_MSK
+#define JI_GPIO_MSK_CLR(_n1) (_n1)
+
+#define REG_GPIO_PAT1(_n1) jz_reg(GPIO_PAT1(_n1))
+#define JA_GPIO_PAT1(_n1) (0xb0010000 + 0x30 + (_n1) * 0x100)
+#define JT_GPIO_PAT1(_n1) JIO_32_RW
+#define JN_GPIO_PAT1(_n1) GPIO_PAT1
+#define JI_GPIO_PAT1(_n1) (_n1)
+#define REG_GPIO_PAT1_SET(_n1) jz_reg(GPIO_PAT1_SET(_n1))
+#define JA_GPIO_PAT1_SET(_n1) (JA_GPIO_PAT1(_n1) + 0x4)
+#define JT_GPIO_PAT1_SET(_n1) JIO_32_WO
+#define JN_GPIO_PAT1_SET(_n1) GPIO_PAT1
+#define JI_GPIO_PAT1_SET(_n1) (_n1)
+#define REG_GPIO_PAT1_CLR(_n1) jz_reg(GPIO_PAT1_CLR(_n1))
+#define JA_GPIO_PAT1_CLR(_n1) (JA_GPIO_PAT1(_n1) + 0x8)
+#define JT_GPIO_PAT1_CLR(_n1) JIO_32_WO
+#define JN_GPIO_PAT1_CLR(_n1) GPIO_PAT1
+#define JI_GPIO_PAT1_CLR(_n1) (_n1)
+
+#define REG_GPIO_PAT0(_n1) jz_reg(GPIO_PAT0(_n1))
+#define JA_GPIO_PAT0(_n1) (0xb0010000 + 0x40 + (_n1) * 0x100)
+#define JT_GPIO_PAT0(_n1) JIO_32_RW
+#define JN_GPIO_PAT0(_n1) GPIO_PAT0
+#define JI_GPIO_PAT0(_n1) (_n1)
+#define REG_GPIO_PAT0_SET(_n1) jz_reg(GPIO_PAT0_SET(_n1))
+#define JA_GPIO_PAT0_SET(_n1) (JA_GPIO_PAT0(_n1) + 0x4)
+#define JT_GPIO_PAT0_SET(_n1) JIO_32_WO
+#define JN_GPIO_PAT0_SET(_n1) GPIO_PAT0
+#define JI_GPIO_PAT0_SET(_n1) (_n1)
+#define REG_GPIO_PAT0_CLR(_n1) jz_reg(GPIO_PAT0_CLR(_n1))
+#define JA_GPIO_PAT0_CLR(_n1) (JA_GPIO_PAT0(_n1) + 0x8)
+#define JT_GPIO_PAT0_CLR(_n1) JIO_32_WO
+#define JN_GPIO_PAT0_CLR(_n1) GPIO_PAT0
+#define JI_GPIO_PAT0_CLR(_n1) (_n1)
+
+#define REG_GPIO_FLAG(_n1) jz_reg(GPIO_FLAG(_n1))
+#define JA_GPIO_FLAG(_n1) (0xb0010000 + 0x50 + (_n1) * 0x100)
+#define JT_GPIO_FLAG(_n1) JIO_32_RW
+#define JN_GPIO_FLAG(_n1) GPIO_FLAG
+#define JI_GPIO_FLAG(_n1) (_n1)
+#define REG_GPIO_FLAG_CLR(_n1) jz_reg(GPIO_FLAG_CLR(_n1))
+#define JA_GPIO_FLAG_CLR(_n1) (JA_GPIO_FLAG(_n1) + 0x8)
+#define JT_GPIO_FLAG_CLR(_n1) JIO_32_WO
+#define JN_GPIO_FLAG_CLR(_n1) GPIO_FLAG
+#define JI_GPIO_FLAG_CLR(_n1) (_n1)
+
+#define REG_GPIO_PULL(_n1) jz_reg(GPIO_PULL(_n1))
+#define JA_GPIO_PULL(_n1) (0xb0010000 + 0x70 + (_n1) * 0x100)
+#define JT_GPIO_PULL(_n1) JIO_32_RW
+#define JN_GPIO_PULL(_n1) GPIO_PULL
+#define JI_GPIO_PULL(_n1) (_n1)
+#define REG_GPIO_PULL_SET(_n1) jz_reg(GPIO_PULL_SET(_n1))
+#define JA_GPIO_PULL_SET(_n1) (JA_GPIO_PULL(_n1) + 0x4)
+#define JT_GPIO_PULL_SET(_n1) JIO_32_WO
+#define JN_GPIO_PULL_SET(_n1) GPIO_PULL
+#define JI_GPIO_PULL_SET(_n1) (_n1)
+#define REG_GPIO_PULL_CLR(_n1) jz_reg(GPIO_PULL_CLR(_n1))
+#define JA_GPIO_PULL_CLR(_n1) (JA_GPIO_PULL(_n1) + 0x8)
+#define JT_GPIO_PULL_CLR(_n1) JIO_32_WO
+#define JN_GPIO_PULL_CLR(_n1) GPIO_PULL
+#define JI_GPIO_PULL_CLR(_n1) (_n1)
+
+#define REG_GPIO_Z_GID2LD jz_reg(GPIO_Z_GID2LD)
+#define JA_GPIO_Z_GID2LD (0xb0010000 + 0x7f0)
+#define JT_GPIO_Z_GID2LD JIO_32_RW
+#define JN_GPIO_Z_GID2LD GPIO_Z_GID2LD
+#define JI_GPIO_Z_GID2LD
+
+#endif /* __HEADERGEN_GPIO_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/i2c.h b/firmware/target/mips/ingenic_x1000/x1000/i2c.h
new file mode 100644
index 0000000000..29f24bf82e
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/i2c.h
@@ -0,0 +1,625 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_I2C_H__
+#define __HEADERGEN_I2C_H__
+
+#include "macro.h"
+
+#define REG_I2C_CON(_n1) jz_reg(I2C_CON(_n1))
+#define JA_I2C_CON(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x0)
+#define JT_I2C_CON(_n1) JIO_32_RW
+#define JN_I2C_CON(_n1) I2C_CON
+#define JI_I2C_CON(_n1) (_n1)
+#define BP_I2C_CON_SPEED 1
+#define BM_I2C_CON_SPEED 0x6
+#define BV_I2C_CON_SPEED__100K 0x1
+#define BV_I2C_CON_SPEED__400K 0x2
+#define BF_I2C_CON_SPEED(v) (((v) & 0x3) << 1)
+#define BFM_I2C_CON_SPEED(v) BM_I2C_CON_SPEED
+#define BF_I2C_CON_SPEED_V(e) BF_I2C_CON_SPEED(BV_I2C_CON_SPEED__##e)
+#define BFM_I2C_CON_SPEED_V(v) BM_I2C_CON_SPEED
+#define BP_I2C_CON_SLVDIS 6
+#define BM_I2C_CON_SLVDIS 0x40
+#define BF_I2C_CON_SLVDIS(v) (((v) & 0x1) << 6)
+#define BFM_I2C_CON_SLVDIS(v) BM_I2C_CON_SLVDIS
+#define BF_I2C_CON_SLVDIS_V(e) BF_I2C_CON_SLVDIS(BV_I2C_CON_SLVDIS__##e)
+#define BFM_I2C_CON_SLVDIS_V(v) BM_I2C_CON_SLVDIS
+#define BP_I2C_CON_RESTART 5
+#define BM_I2C_CON_RESTART 0x20
+#define BF_I2C_CON_RESTART(v) (((v) & 0x1) << 5)
+#define BFM_I2C_CON_RESTART(v) BM_I2C_CON_RESTART
+#define BF_I2C_CON_RESTART_V(e) BF_I2C_CON_RESTART(BV_I2C_CON_RESTART__##e)
+#define BFM_I2C_CON_RESTART_V(v) BM_I2C_CON_RESTART
+#define BP_I2C_CON_MATP 4
+#define BM_I2C_CON_MATP 0x10
+#define BF_I2C_CON_MATP(v) (((v) & 0x1) << 4)
+#define BFM_I2C_CON_MATP(v) BM_I2C_CON_MATP
+#define BF_I2C_CON_MATP_V(e) BF_I2C_CON_MATP(BV_I2C_CON_MATP__##e)
+#define BFM_I2C_CON_MATP_V(v) BM_I2C_CON_MATP
+#define BP_I2C_CON_SATP 3
+#define BM_I2C_CON_SATP 0x8
+#define BF_I2C_CON_SATP(v) (((v) & 0x1) << 3)
+#define BFM_I2C_CON_SATP(v) BM_I2C_CON_SATP
+#define BF_I2C_CON_SATP_V(e) BF_I2C_CON_SATP(BV_I2C_CON_SATP__##e)
+#define BFM_I2C_CON_SATP_V(v) BM_I2C_CON_SATP
+#define BP_I2C_CON_MD 0
+#define BM_I2C_CON_MD 0x1
+#define BF_I2C_CON_MD(v) (((v) & 0x1) << 0)
+#define BFM_I2C_CON_MD(v) BM_I2C_CON_MD
+#define BF_I2C_CON_MD_V(e) BF_I2C_CON_MD(BV_I2C_CON_MD__##e)
+#define BFM_I2C_CON_MD_V(v) BM_I2C_CON_MD
+
+#define REG_I2C_DC(_n1) jz_reg(I2C_DC(_n1))
+#define JA_I2C_DC(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x10)
+#define JT_I2C_DC(_n1) JIO_32_RW
+#define JN_I2C_DC(_n1) I2C_DC
+#define JI_I2C_DC(_n1) (_n1)
+#define BP_I2C_DC_DAT 0
+#define BM_I2C_DC_DAT 0xff
+#define BF_I2C_DC_DAT(v) (((v) & 0xff) << 0)
+#define BFM_I2C_DC_DAT(v) BM_I2C_DC_DAT
+#define BF_I2C_DC_DAT_V(e) BF_I2C_DC_DAT(BV_I2C_DC_DAT__##e)
+#define BFM_I2C_DC_DAT_V(v) BM_I2C_DC_DAT
+#define BP_I2C_DC_RESTART 10
+#define BM_I2C_DC_RESTART 0x400
+#define BF_I2C_DC_RESTART(v) (((v) & 0x1) << 10)
+#define BFM_I2C_DC_RESTART(v) BM_I2C_DC_RESTART
+#define BF_I2C_DC_RESTART_V(e) BF_I2C_DC_RESTART(BV_I2C_DC_RESTART__##e)
+#define BFM_I2C_DC_RESTART_V(v) BM_I2C_DC_RESTART
+#define BP_I2C_DC_STOP 9
+#define BM_I2C_DC_STOP 0x200
+#define BF_I2C_DC_STOP(v) (((v) & 0x1) << 9)
+#define BFM_I2C_DC_STOP(v) BM_I2C_DC_STOP
+#define BF_I2C_DC_STOP_V(e) BF_I2C_DC_STOP(BV_I2C_DC_STOP__##e)
+#define BFM_I2C_DC_STOP_V(v) BM_I2C_DC_STOP
+#define BP_I2C_DC_CMD 8
+#define BM_I2C_DC_CMD 0x100
+#define BF_I2C_DC_CMD(v) (((v) & 0x1) << 8)
+#define BFM_I2C_DC_CMD(v) BM_I2C_DC_CMD
+#define BF_I2C_DC_CMD_V(e) BF_I2C_DC_CMD(BV_I2C_DC_CMD__##e)
+#define BFM_I2C_DC_CMD_V(v) BM_I2C_DC_CMD
+
+#define REG_I2C_INTST(_n1) jz_reg(I2C_INTST(_n1))
+#define JA_I2C_INTST(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x2c)
+#define JT_I2C_INTST(_n1) JIO_32_RW
+#define JN_I2C_INTST(_n1) I2C_INTST
+#define JI_I2C_INTST(_n1) (_n1)
+#define BP_I2C_INTST_GC 11
+#define BM_I2C_INTST_GC 0x800
+#define BF_I2C_INTST_GC(v) (((v) & 0x1) << 11)
+#define BFM_I2C_INTST_GC(v) BM_I2C_INTST_GC
+#define BF_I2C_INTST_GC_V(e) BF_I2C_INTST_GC(BV_I2C_INTST_GC__##e)
+#define BFM_I2C_INTST_GC_V(v) BM_I2C_INTST_GC
+#define BP_I2C_INTST_STT 10
+#define BM_I2C_INTST_STT 0x400
+#define BF_I2C_INTST_STT(v) (((v) & 0x1) << 10)
+#define BFM_I2C_INTST_STT(v) BM_I2C_INTST_STT
+#define BF_I2C_INTST_STT_V(e) BF_I2C_INTST_STT(BV_I2C_INTST_STT__##e)
+#define BFM_I2C_INTST_STT_V(v) BM_I2C_INTST_STT
+#define BP_I2C_INTST_STP 9
+#define BM_I2C_INTST_STP 0x200
+#define BF_I2C_INTST_STP(v) (((v) & 0x1) << 9)
+#define BFM_I2C_INTST_STP(v) BM_I2C_INTST_STP
+#define BF_I2C_INTST_STP_V(e) BF_I2C_INTST_STP(BV_I2C_INTST_STP__##e)
+#define BFM_I2C_INTST_STP_V(v) BM_I2C_INTST_STP
+#define BP_I2C_INTST_ACT 8
+#define BM_I2C_INTST_ACT 0x100
+#define BF_I2C_INTST_ACT(v) (((v) & 0x1) << 8)
+#define BFM_I2C_INTST_ACT(v) BM_I2C_INTST_ACT
+#define BF_I2C_INTST_ACT_V(e) BF_I2C_INTST_ACT(BV_I2C_INTST_ACT__##e)
+#define BFM_I2C_INTST_ACT_V(v) BM_I2C_INTST_ACT
+#define BP_I2C_INTST_RXDN 7
+#define BM_I2C_INTST_RXDN 0x80
+#define BF_I2C_INTST_RXDN(v) (((v) & 0x1) << 7)
+#define BFM_I2C_INTST_RXDN(v) BM_I2C_INTST_RXDN
+#define BF_I2C_INTST_RXDN_V(e) BF_I2C_INTST_RXDN(BV_I2C_INTST_RXDN__##e)
+#define BFM_I2C_INTST_RXDN_V(v) BM_I2C_INTST_RXDN
+#define BP_I2C_INTST_TXABT 6
+#define BM_I2C_INTST_TXABT 0x40
+#define BF_I2C_INTST_TXABT(v) (((v) & 0x1) << 6)
+#define BFM_I2C_INTST_TXABT(v) BM_I2C_INTST_TXABT
+#define BF_I2C_INTST_TXABT_V(e) BF_I2C_INTST_TXABT(BV_I2C_INTST_TXABT__##e)
+#define BFM_I2C_INTST_TXABT_V(v) BM_I2C_INTST_TXABT
+#define BP_I2C_INTST_RDREQ 5
+#define BM_I2C_INTST_RDREQ 0x20
+#define BF_I2C_INTST_RDREQ(v) (((v) & 0x1) << 5)
+#define BFM_I2C_INTST_RDREQ(v) BM_I2C_INTST_RDREQ
+#define BF_I2C_INTST_RDREQ_V(e) BF_I2C_INTST_RDREQ(BV_I2C_INTST_RDREQ__##e)
+#define BFM_I2C_INTST_RDREQ_V(v) BM_I2C_INTST_RDREQ
+#define BP_I2C_INTST_TXEMP 4
+#define BM_I2C_INTST_TXEMP 0x10
+#define BF_I2C_INTST_TXEMP(v) (((v) & 0x1) << 4)
+#define BFM_I2C_INTST_TXEMP(v) BM_I2C_INTST_TXEMP
+#define BF_I2C_INTST_TXEMP_V(e) BF_I2C_INTST_TXEMP(BV_I2C_INTST_TXEMP__##e)
+#define BFM_I2C_INTST_TXEMP_V(v) BM_I2C_INTST_TXEMP
+#define BP_I2C_INTST_TXOF 3
+#define BM_I2C_INTST_TXOF 0x8
+#define BF_I2C_INTST_TXOF(v) (((v) & 0x1) << 3)
+#define BFM_I2C_INTST_TXOF(v) BM_I2C_INTST_TXOF
+#define BF_I2C_INTST_TXOF_V(e) BF_I2C_INTST_TXOF(BV_I2C_INTST_TXOF__##e)
+#define BFM_I2C_INTST_TXOF_V(v) BM_I2C_INTST_TXOF
+#define BP_I2C_INTST_RXFL 2
+#define BM_I2C_INTST_RXFL 0x4
+#define BF_I2C_INTST_RXFL(v) (((v) & 0x1) << 2)
+#define BFM_I2C_INTST_RXFL(v) BM_I2C_INTST_RXFL
+#define BF_I2C_INTST_RXFL_V(e) BF_I2C_INTST_RXFL(BV_I2C_INTST_RXFL__##e)
+#define BFM_I2C_INTST_RXFL_V(v) BM_I2C_INTST_RXFL
+#define BP_I2C_INTST_RXOF 1
+#define BM_I2C_INTST_RXOF 0x2
+#define BF_I2C_INTST_RXOF(v) (((v) & 0x1) << 1)
+#define BFM_I2C_INTST_RXOF(v) BM_I2C_INTST_RXOF
+#define BF_I2C_INTST_RXOF_V(e) BF_I2C_INTST_RXOF(BV_I2C_INTST_RXOF__##e)
+#define BFM_I2C_INTST_RXOF_V(v) BM_I2C_INTST_RXOF
+#define BP_I2C_INTST_RXUF 0
+#define BM_I2C_INTST_RXUF 0x1
+#define BF_I2C_INTST_RXUF(v) (((v) & 0x1) << 0)
+#define BFM_I2C_INTST_RXUF(v) BM_I2C_INTST_RXUF
+#define BF_I2C_INTST_RXUF_V(e) BF_I2C_INTST_RXUF(BV_I2C_INTST_RXUF__##e)
+#define BFM_I2C_INTST_RXUF_V(v) BM_I2C_INTST_RXUF
+
+#define REG_I2C_INTMSK(_n1) jz_reg(I2C_INTMSK(_n1))
+#define JA_I2C_INTMSK(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x30)
+#define JT_I2C_INTMSK(_n1) JIO_32_RW
+#define JN_I2C_INTMSK(_n1) I2C_INTMSK
+#define JI_I2C_INTMSK(_n1) (_n1)
+#define BP_I2C_INTMSK_GC 11
+#define BM_I2C_INTMSK_GC 0x800
+#define BF_I2C_INTMSK_GC(v) (((v) & 0x1) << 11)
+#define BFM_I2C_INTMSK_GC(v) BM_I2C_INTMSK_GC
+#define BF_I2C_INTMSK_GC_V(e) BF_I2C_INTMSK_GC(BV_I2C_INTMSK_GC__##e)
+#define BFM_I2C_INTMSK_GC_V(v) BM_I2C_INTMSK_GC
+#define BP_I2C_INTMSK_STT 10
+#define BM_I2C_INTMSK_STT 0x400
+#define BF_I2C_INTMSK_STT(v) (((v) & 0x1) << 10)
+#define BFM_I2C_INTMSK_STT(v) BM_I2C_INTMSK_STT
+#define BF_I2C_INTMSK_STT_V(e) BF_I2C_INTMSK_STT(BV_I2C_INTMSK_STT__##e)
+#define BFM_I2C_INTMSK_STT_V(v) BM_I2C_INTMSK_STT
+#define BP_I2C_INTMSK_STP 9
+#define BM_I2C_INTMSK_STP 0x200
+#define BF_I2C_INTMSK_STP(v) (((v) & 0x1) << 9)
+#define BFM_I2C_INTMSK_STP(v) BM_I2C_INTMSK_STP
+#define BF_I2C_INTMSK_STP_V(e) BF_I2C_INTMSK_STP(BV_I2C_INTMSK_STP__##e)
+#define BFM_I2C_INTMSK_STP_V(v) BM_I2C_INTMSK_STP
+#define BP_I2C_INTMSK_ACT 8
+#define BM_I2C_INTMSK_ACT 0x100
+#define BF_I2C_INTMSK_ACT(v) (((v) & 0x1) << 8)
+#define BFM_I2C_INTMSK_ACT(v) BM_I2C_INTMSK_ACT
+#define BF_I2C_INTMSK_ACT_V(e) BF_I2C_INTMSK_ACT(BV_I2C_INTMSK_ACT__##e)
+#define BFM_I2C_INTMSK_ACT_V(v) BM_I2C_INTMSK_ACT
+#define BP_I2C_INTMSK_RXDN 7
+#define BM_I2C_INTMSK_RXDN 0x80
+#define BF_I2C_INTMSK_RXDN(v) (((v) & 0x1) << 7)
+#define BFM_I2C_INTMSK_RXDN(v) BM_I2C_INTMSK_RXDN
+#define BF_I2C_INTMSK_RXDN_V(e) BF_I2C_INTMSK_RXDN(BV_I2C_INTMSK_RXDN__##e)
+#define BFM_I2C_INTMSK_RXDN_V(v) BM_I2C_INTMSK_RXDN
+#define BP_I2C_INTMSK_TXABT 6
+#define BM_I2C_INTMSK_TXABT 0x40
+#define BF_I2C_INTMSK_TXABT(v) (((v) & 0x1) << 6)
+#define BFM_I2C_INTMSK_TXABT(v) BM_I2C_INTMSK_TXABT
+#define BF_I2C_INTMSK_TXABT_V(e) BF_I2C_INTMSK_TXABT(BV_I2C_INTMSK_TXABT__##e)
+#define BFM_I2C_INTMSK_TXABT_V(v) BM_I2C_INTMSK_TXABT
+#define BP_I2C_INTMSK_RDREQ 5
+#define BM_I2C_INTMSK_RDREQ 0x20
+#define BF_I2C_INTMSK_RDREQ(v) (((v) & 0x1) << 5)
+#define BFM_I2C_INTMSK_RDREQ(v) BM_I2C_INTMSK_RDREQ
+#define BF_I2C_INTMSK_RDREQ_V(e) BF_I2C_INTMSK_RDREQ(BV_I2C_INTMSK_RDREQ__##e)
+#define BFM_I2C_INTMSK_RDREQ_V(v) BM_I2C_INTMSK_RDREQ
+#define BP_I2C_INTMSK_TXEMP 4
+#define BM_I2C_INTMSK_TXEMP 0x10
+#define BF_I2C_INTMSK_TXEMP(v) (((v) & 0x1) << 4)
+#define BFM_I2C_INTMSK_TXEMP(v) BM_I2C_INTMSK_TXEMP
+#define BF_I2C_INTMSK_TXEMP_V(e) BF_I2C_INTMSK_TXEMP(BV_I2C_INTMSK_TXEMP__##e)
+#define BFM_I2C_INTMSK_TXEMP_V(v) BM_I2C_INTMSK_TXEMP
+#define BP_I2C_INTMSK_TXOF 3
+#define BM_I2C_INTMSK_TXOF 0x8
+#define BF_I2C_INTMSK_TXOF(v) (((v) & 0x1) << 3)
+#define BFM_I2C_INTMSK_TXOF(v) BM_I2C_INTMSK_TXOF
+#define BF_I2C_INTMSK_TXOF_V(e) BF_I2C_INTMSK_TXOF(BV_I2C_INTMSK_TXOF__##e)
+#define BFM_I2C_INTMSK_TXOF_V(v) BM_I2C_INTMSK_TXOF
+#define BP_I2C_INTMSK_RXFL 2
+#define BM_I2C_INTMSK_RXFL 0x4
+#define BF_I2C_INTMSK_RXFL(v) (((v) & 0x1) << 2)
+#define BFM_I2C_INTMSK_RXFL(v) BM_I2C_INTMSK_RXFL
+#define BF_I2C_INTMSK_RXFL_V(e) BF_I2C_INTMSK_RXFL(BV_I2C_INTMSK_RXFL__##e)
+#define BFM_I2C_INTMSK_RXFL_V(v) BM_I2C_INTMSK_RXFL
+#define BP_I2C_INTMSK_RXOF 1
+#define BM_I2C_INTMSK_RXOF 0x2
+#define BF_I2C_INTMSK_RXOF(v) (((v) & 0x1) << 1)
+#define BFM_I2C_INTMSK_RXOF(v) BM_I2C_INTMSK_RXOF
+#define BF_I2C_INTMSK_RXOF_V(e) BF_I2C_INTMSK_RXOF(BV_I2C_INTMSK_RXOF__##e)
+#define BFM_I2C_INTMSK_RXOF_V(v) BM_I2C_INTMSK_RXOF
+#define BP_I2C_INTMSK_RXUF 0
+#define BM_I2C_INTMSK_RXUF 0x1
+#define BF_I2C_INTMSK_RXUF(v) (((v) & 0x1) << 0)
+#define BFM_I2C_INTMSK_RXUF(v) BM_I2C_INTMSK_RXUF
+#define BF_I2C_INTMSK_RXUF_V(e) BF_I2C_INTMSK_RXUF(BV_I2C_INTMSK_RXUF__##e)
+#define BFM_I2C_INTMSK_RXUF_V(v) BM_I2C_INTMSK_RXUF
+
+#define REG_I2C_RINTST(_n1) jz_reg(I2C_RINTST(_n1))
+#define JA_I2C_RINTST(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x34)
+#define JT_I2C_RINTST(_n1) JIO_32_RW
+#define JN_I2C_RINTST(_n1) I2C_RINTST
+#define JI_I2C_RINTST(_n1) (_n1)
+#define BP_I2C_RINTST_GC 11
+#define BM_I2C_RINTST_GC 0x800
+#define BF_I2C_RINTST_GC(v) (((v) & 0x1) << 11)
+#define BFM_I2C_RINTST_GC(v) BM_I2C_RINTST_GC
+#define BF_I2C_RINTST_GC_V(e) BF_I2C_RINTST_GC(BV_I2C_RINTST_GC__##e)
+#define BFM_I2C_RINTST_GC_V(v) BM_I2C_RINTST_GC
+#define BP_I2C_RINTST_STT 10
+#define BM_I2C_RINTST_STT 0x400
+#define BF_I2C_RINTST_STT(v) (((v) & 0x1) << 10)
+#define BFM_I2C_RINTST_STT(v) BM_I2C_RINTST_STT
+#define BF_I2C_RINTST_STT_V(e) BF_I2C_RINTST_STT(BV_I2C_RINTST_STT__##e)
+#define BFM_I2C_RINTST_STT_V(v) BM_I2C_RINTST_STT
+#define BP_I2C_RINTST_STP 9
+#define BM_I2C_RINTST_STP 0x200
+#define BF_I2C_RINTST_STP(v) (((v) & 0x1) << 9)
+#define BFM_I2C_RINTST_STP(v) BM_I2C_RINTST_STP
+#define BF_I2C_RINTST_STP_V(e) BF_I2C_RINTST_STP(BV_I2C_RINTST_STP__##e)
+#define BFM_I2C_RINTST_STP_V(v) BM_I2C_RINTST_STP
+#define BP_I2C_RINTST_ACT 8
+#define BM_I2C_RINTST_ACT 0x100
+#define BF_I2C_RINTST_ACT(v) (((v) & 0x1) << 8)
+#define BFM_I2C_RINTST_ACT(v) BM_I2C_RINTST_ACT
+#define BF_I2C_RINTST_ACT_V(e) BF_I2C_RINTST_ACT(BV_I2C_RINTST_ACT__##e)
+#define BFM_I2C_RINTST_ACT_V(v) BM_I2C_RINTST_ACT
+#define BP_I2C_RINTST_RXDN 7
+#define BM_I2C_RINTST_RXDN 0x80
+#define BF_I2C_RINTST_RXDN(v) (((v) & 0x1) << 7)
+#define BFM_I2C_RINTST_RXDN(v) BM_I2C_RINTST_RXDN
+#define BF_I2C_RINTST_RXDN_V(e) BF_I2C_RINTST_RXDN(BV_I2C_RINTST_RXDN__##e)
+#define BFM_I2C_RINTST_RXDN_V(v) BM_I2C_RINTST_RXDN
+#define BP_I2C_RINTST_TXABT 6
+#define BM_I2C_RINTST_TXABT 0x40
+#define BF_I2C_RINTST_TXABT(v) (((v) & 0x1) << 6)
+#define BFM_I2C_RINTST_TXABT(v) BM_I2C_RINTST_TXABT
+#define BF_I2C_RINTST_TXABT_V(e) BF_I2C_RINTST_TXABT(BV_I2C_RINTST_TXABT__##e)
+#define BFM_I2C_RINTST_TXABT_V(v) BM_I2C_RINTST_TXABT
+#define BP_I2C_RINTST_RDREQ 5
+#define BM_I2C_RINTST_RDREQ 0x20
+#define BF_I2C_RINTST_RDREQ(v) (((v) & 0x1) << 5)
+#define BFM_I2C_RINTST_RDREQ(v) BM_I2C_RINTST_RDREQ
+#define BF_I2C_RINTST_RDREQ_V(e) BF_I2C_RINTST_RDREQ(BV_I2C_RINTST_RDREQ__##e)
+#define BFM_I2C_RINTST_RDREQ_V(v) BM_I2C_RINTST_RDREQ
+#define BP_I2C_RINTST_TXEMP 4
+#define BM_I2C_RINTST_TXEMP 0x10
+#define BF_I2C_RINTST_TXEMP(v) (((v) & 0x1) << 4)
+#define BFM_I2C_RINTST_TXEMP(v) BM_I2C_RINTST_TXEMP
+#define BF_I2C_RINTST_TXEMP_V(e) BF_I2C_RINTST_TXEMP(BV_I2C_RINTST_TXEMP__##e)
+#define BFM_I2C_RINTST_TXEMP_V(v) BM_I2C_RINTST_TXEMP
+#define BP_I2C_RINTST_TXOF 3
+#define BM_I2C_RINTST_TXOF 0x8
+#define BF_I2C_RINTST_TXOF(v) (((v) & 0x1) << 3)
+#define BFM_I2C_RINTST_TXOF(v) BM_I2C_RINTST_TXOF
+#define BF_I2C_RINTST_TXOF_V(e) BF_I2C_RINTST_TXOF(BV_I2C_RINTST_TXOF__##e)
+#define BFM_I2C_RINTST_TXOF_V(v) BM_I2C_RINTST_TXOF
+#define BP_I2C_RINTST_RXFL 2
+#define BM_I2C_RINTST_RXFL 0x4
+#define BF_I2C_RINTST_RXFL(v) (((v) & 0x1) << 2)
+#define BFM_I2C_RINTST_RXFL(v) BM_I2C_RINTST_RXFL
+#define BF_I2C_RINTST_RXFL_V(e) BF_I2C_RINTST_RXFL(BV_I2C_RINTST_RXFL__##e)
+#define BFM_I2C_RINTST_RXFL_V(v) BM_I2C_RINTST_RXFL
+#define BP_I2C_RINTST_RXOF 1
+#define BM_I2C_RINTST_RXOF 0x2
+#define BF_I2C_RINTST_RXOF(v) (((v) & 0x1) << 1)
+#define BFM_I2C_RINTST_RXOF(v) BM_I2C_RINTST_RXOF
+#define BF_I2C_RINTST_RXOF_V(e) BF_I2C_RINTST_RXOF(BV_I2C_RINTST_RXOF__##e)
+#define BFM_I2C_RINTST_RXOF_V(v) BM_I2C_RINTST_RXOF
+#define BP_I2C_RINTST_RXUF 0
+#define BM_I2C_RINTST_RXUF 0x1
+#define BF_I2C_RINTST_RXUF(v) (((v) & 0x1) << 0)
+#define BFM_I2C_RINTST_RXUF(v) BM_I2C_RINTST_RXUF
+#define BF_I2C_RINTST_RXUF_V(e) BF_I2C_RINTST_RXUF(BV_I2C_RINTST_RXUF__##e)
+#define BFM_I2C_RINTST_RXUF_V(v) BM_I2C_RINTST_RXUF
+
+#define REG_I2C_ENABLE(_n1) jz_reg(I2C_ENABLE(_n1))
+#define JA_I2C_ENABLE(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x6c)
+#define JT_I2C_ENABLE(_n1) JIO_32_RW
+#define JN_I2C_ENABLE(_n1) I2C_ENABLE
+#define JI_I2C_ENABLE(_n1) (_n1)
+#define BP_I2C_ENABLE_ABORT 1
+#define BM_I2C_ENABLE_ABORT 0x2
+#define BF_I2C_ENABLE_ABORT(v) (((v) & 0x1) << 1)
+#define BFM_I2C_ENABLE_ABORT(v) BM_I2C_ENABLE_ABORT
+#define BF_I2C_ENABLE_ABORT_V(e) BF_I2C_ENABLE_ABORT(BV_I2C_ENABLE_ABORT__##e)
+#define BFM_I2C_ENABLE_ABORT_V(v) BM_I2C_ENABLE_ABORT
+#define BP_I2C_ENABLE_ACTIVE 0
+#define BM_I2C_ENABLE_ACTIVE 0x1
+#define BF_I2C_ENABLE_ACTIVE(v) (((v) & 0x1) << 0)
+#define BFM_I2C_ENABLE_ACTIVE(v) BM_I2C_ENABLE_ACTIVE
+#define BF_I2C_ENABLE_ACTIVE_V(e) BF_I2C_ENABLE_ACTIVE(BV_I2C_ENABLE_ACTIVE__##e)
+#define BFM_I2C_ENABLE_ACTIVE_V(v) BM_I2C_ENABLE_ACTIVE
+
+#define REG_I2C_STATUS(_n1) jz_reg(I2C_STATUS(_n1))
+#define JA_I2C_STATUS(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x70)
+#define JT_I2C_STATUS(_n1) JIO_32_RW
+#define JN_I2C_STATUS(_n1) I2C_STATUS
+#define JI_I2C_STATUS(_n1) (_n1)
+#define BP_I2C_STATUS_SLVACT 6
+#define BM_I2C_STATUS_SLVACT 0x40
+#define BF_I2C_STATUS_SLVACT(v) (((v) & 0x1) << 6)
+#define BFM_I2C_STATUS_SLVACT(v) BM_I2C_STATUS_SLVACT
+#define BF_I2C_STATUS_SLVACT_V(e) BF_I2C_STATUS_SLVACT(BV_I2C_STATUS_SLVACT__##e)
+#define BFM_I2C_STATUS_SLVACT_V(v) BM_I2C_STATUS_SLVACT
+#define BP_I2C_STATUS_MSTACT 5
+#define BM_I2C_STATUS_MSTACT 0x20
+#define BF_I2C_STATUS_MSTACT(v) (((v) & 0x1) << 5)
+#define BFM_I2C_STATUS_MSTACT(v) BM_I2C_STATUS_MSTACT
+#define BF_I2C_STATUS_MSTACT_V(e) BF_I2C_STATUS_MSTACT(BV_I2C_STATUS_MSTACT__##e)
+#define BFM_I2C_STATUS_MSTACT_V(v) BM_I2C_STATUS_MSTACT
+#define BP_I2C_STATUS_RFF 4
+#define BM_I2C_STATUS_RFF 0x10
+#define BF_I2C_STATUS_RFF(v) (((v) & 0x1) << 4)
+#define BFM_I2C_STATUS_RFF(v) BM_I2C_STATUS_RFF
+#define BF_I2C_STATUS_RFF_V(e) BF_I2C_STATUS_RFF(BV_I2C_STATUS_RFF__##e)
+#define BFM_I2C_STATUS_RFF_V(v) BM_I2C_STATUS_RFF
+#define BP_I2C_STATUS_RFNE 3
+#define BM_I2C_STATUS_RFNE 0x8
+#define BF_I2C_STATUS_RFNE(v) (((v) & 0x1) << 3)
+#define BFM_I2C_STATUS_RFNE(v) BM_I2C_STATUS_RFNE
+#define BF_I2C_STATUS_RFNE_V(e) BF_I2C_STATUS_RFNE(BV_I2C_STATUS_RFNE__##e)
+#define BFM_I2C_STATUS_RFNE_V(v) BM_I2C_STATUS_RFNE
+#define BP_I2C_STATUS_TFE 2
+#define BM_I2C_STATUS_TFE 0x4
+#define BF_I2C_STATUS_TFE(v) (((v) & 0x1) << 2)
+#define BFM_I2C_STATUS_TFE(v) BM_I2C_STATUS_TFE
+#define BF_I2C_STATUS_TFE_V(e) BF_I2C_STATUS_TFE(BV_I2C_STATUS_TFE__##e)
+#define BFM_I2C_STATUS_TFE_V(v) BM_I2C_STATUS_TFE
+#define BP_I2C_STATUS_TFNF 1
+#define BM_I2C_STATUS_TFNF 0x2
+#define BF_I2C_STATUS_TFNF(v) (((v) & 0x1) << 1)
+#define BFM_I2C_STATUS_TFNF(v) BM_I2C_STATUS_TFNF
+#define BF_I2C_STATUS_TFNF_V(e) BF_I2C_STATUS_TFNF(BV_I2C_STATUS_TFNF__##e)
+#define BFM_I2C_STATUS_TFNF_V(v) BM_I2C_STATUS_TFNF
+#define BP_I2C_STATUS_ACT 0
+#define BM_I2C_STATUS_ACT 0x1
+#define BF_I2C_STATUS_ACT(v) (((v) & 0x1) << 0)
+#define BFM_I2C_STATUS_ACT(v) BM_I2C_STATUS_ACT
+#define BF_I2C_STATUS_ACT_V(e) BF_I2C_STATUS_ACT(BV_I2C_STATUS_ACT__##e)
+#define BFM_I2C_STATUS_ACT_V(v) BM_I2C_STATUS_ACT
+
+#define REG_I2C_ENBST(_n1) jz_reg(I2C_ENBST(_n1))
+#define JA_I2C_ENBST(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x9c)
+#define JT_I2C_ENBST(_n1) JIO_32_RW
+#define JN_I2C_ENBST(_n1) I2C_ENBST
+#define JI_I2C_ENBST(_n1) (_n1)
+#define BP_I2C_ENBST_SLVRDLST 2
+#define BM_I2C_ENBST_SLVRDLST 0x4
+#define BF_I2C_ENBST_SLVRDLST(v) (((v) & 0x1) << 2)
+#define BFM_I2C_ENBST_SLVRDLST(v) BM_I2C_ENBST_SLVRDLST
+#define BF_I2C_ENBST_SLVRDLST_V(e) BF_I2C_ENBST_SLVRDLST(BV_I2C_ENBST_SLVRDLST__##e)
+#define BFM_I2C_ENBST_SLVRDLST_V(v) BM_I2C_ENBST_SLVRDLST
+#define BP_I2C_ENBST_SLVDISB 1
+#define BM_I2C_ENBST_SLVDISB 0x2
+#define BF_I2C_ENBST_SLVDISB(v) (((v) & 0x1) << 1)
+#define BFM_I2C_ENBST_SLVDISB(v) BM_I2C_ENBST_SLVDISB
+#define BF_I2C_ENBST_SLVDISB_V(e) BF_I2C_ENBST_SLVDISB(BV_I2C_ENBST_SLVDISB__##e)
+#define BFM_I2C_ENBST_SLVDISB_V(v) BM_I2C_ENBST_SLVDISB
+#define BP_I2C_ENBST_ACTIVE 0
+#define BM_I2C_ENBST_ACTIVE 0x1
+#define BF_I2C_ENBST_ACTIVE(v) (((v) & 0x1) << 0)
+#define BFM_I2C_ENBST_ACTIVE(v) BM_I2C_ENBST_ACTIVE
+#define BF_I2C_ENBST_ACTIVE_V(e) BF_I2C_ENBST_ACTIVE(BV_I2C_ENBST_ACTIVE__##e)
+#define BFM_I2C_ENBST_ACTIVE_V(v) BM_I2C_ENBST_ACTIVE
+
+#define REG_I2C_TAR(_n1) jz_reg(I2C_TAR(_n1))
+#define JA_I2C_TAR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x4)
+#define JT_I2C_TAR(_n1) JIO_32_RW
+#define JN_I2C_TAR(_n1) I2C_TAR
+#define JI_I2C_TAR(_n1) (_n1)
+#define BP_I2C_TAR_ADDR 0
+#define BM_I2C_TAR_ADDR 0x3ff
+#define BF_I2C_TAR_ADDR(v) (((v) & 0x3ff) << 0)
+#define BFM_I2C_TAR_ADDR(v) BM_I2C_TAR_ADDR
+#define BF_I2C_TAR_ADDR_V(e) BF_I2C_TAR_ADDR(BV_I2C_TAR_ADDR__##e)
+#define BFM_I2C_TAR_ADDR_V(v) BM_I2C_TAR_ADDR
+#define BP_I2C_TAR_10BITS 12
+#define BM_I2C_TAR_10BITS 0x1000
+#define BF_I2C_TAR_10BITS(v) (((v) & 0x1) << 12)
+#define BFM_I2C_TAR_10BITS(v) BM_I2C_TAR_10BITS
+#define BF_I2C_TAR_10BITS_V(e) BF_I2C_TAR_10BITS(BV_I2C_TAR_10BITS__##e)
+#define BFM_I2C_TAR_10BITS_V(v) BM_I2C_TAR_10BITS
+#define BP_I2C_TAR_SPECIAL 11
+#define BM_I2C_TAR_SPECIAL 0x800
+#define BF_I2C_TAR_SPECIAL(v) (((v) & 0x1) << 11)
+#define BFM_I2C_TAR_SPECIAL(v) BM_I2C_TAR_SPECIAL
+#define BF_I2C_TAR_SPECIAL_V(e) BF_I2C_TAR_SPECIAL(BV_I2C_TAR_SPECIAL__##e)
+#define BFM_I2C_TAR_SPECIAL_V(v) BM_I2C_TAR_SPECIAL
+#define BP_I2C_TAR_GC_OR_START 10
+#define BM_I2C_TAR_GC_OR_START 0x400
+#define BF_I2C_TAR_GC_OR_START(v) (((v) & 0x1) << 10)
+#define BFM_I2C_TAR_GC_OR_START(v) BM_I2C_TAR_GC_OR_START
+#define BF_I2C_TAR_GC_OR_START_V(e) BF_I2C_TAR_GC_OR_START(BV_I2C_TAR_GC_OR_START__##e)
+#define BFM_I2C_TAR_GC_OR_START_V(v) BM_I2C_TAR_GC_OR_START
+
+#define REG_I2C_SAR(_n1) jz_reg(I2C_SAR(_n1))
+#define JA_I2C_SAR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x8)
+#define JT_I2C_SAR(_n1) JIO_32_RW
+#define JN_I2C_SAR(_n1) I2C_SAR
+#define JI_I2C_SAR(_n1) (_n1)
+
+#define REG_I2C_SHCNT(_n1) jz_reg(I2C_SHCNT(_n1))
+#define JA_I2C_SHCNT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x14)
+#define JT_I2C_SHCNT(_n1) JIO_32_RW
+#define JN_I2C_SHCNT(_n1) I2C_SHCNT
+#define JI_I2C_SHCNT(_n1) (_n1)
+
+#define REG_I2C_SLCNT(_n1) jz_reg(I2C_SLCNT(_n1))
+#define JA_I2C_SLCNT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x18)
+#define JT_I2C_SLCNT(_n1) JIO_32_RW
+#define JN_I2C_SLCNT(_n1) I2C_SLCNT
+#define JI_I2C_SLCNT(_n1) (_n1)
+
+#define REG_I2C_FHCNT(_n1) jz_reg(I2C_FHCNT(_n1))
+#define JA_I2C_FHCNT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x1c)
+#define JT_I2C_FHCNT(_n1) JIO_32_RW
+#define JN_I2C_FHCNT(_n1) I2C_FHCNT
+#define JI_I2C_FHCNT(_n1) (_n1)
+
+#define REG_I2C_FLCNT(_n1) jz_reg(I2C_FLCNT(_n1))
+#define JA_I2C_FLCNT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x20)
+#define JT_I2C_FLCNT(_n1) JIO_32_RW
+#define JN_I2C_FLCNT(_n1) I2C_FLCNT
+#define JI_I2C_FLCNT(_n1) (_n1)
+
+#define REG_I2C_RXTL(_n1) jz_reg(I2C_RXTL(_n1))
+#define JA_I2C_RXTL(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x38)
+#define JT_I2C_RXTL(_n1) JIO_32_RW
+#define JN_I2C_RXTL(_n1) I2C_RXTL
+#define JI_I2C_RXTL(_n1) (_n1)
+
+#define REG_I2C_TXTL(_n1) jz_reg(I2C_TXTL(_n1))
+#define JA_I2C_TXTL(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x3c)
+#define JT_I2C_TXTL(_n1) JIO_32_RW
+#define JN_I2C_TXTL(_n1) I2C_TXTL
+#define JI_I2C_TXTL(_n1) (_n1)
+
+#define REG_I2C_TXFLR(_n1) jz_reg(I2C_TXFLR(_n1))
+#define JA_I2C_TXFLR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x74)
+#define JT_I2C_TXFLR(_n1) JIO_32_RW
+#define JN_I2C_TXFLR(_n1) I2C_TXFLR
+#define JI_I2C_TXFLR(_n1) (_n1)
+
+#define REG_I2C_RXFLR(_n1) jz_reg(I2C_RXFLR(_n1))
+#define JA_I2C_RXFLR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x78)
+#define JT_I2C_RXFLR(_n1) JIO_32_RW
+#define JN_I2C_RXFLR(_n1) I2C_RXFLR
+#define JI_I2C_RXFLR(_n1) (_n1)
+
+#define REG_I2C_SDAHD(_n1) jz_reg(I2C_SDAHD(_n1))
+#define JA_I2C_SDAHD(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x7c)
+#define JT_I2C_SDAHD(_n1) JIO_32_RW
+#define JN_I2C_SDAHD(_n1) I2C_SDAHD
+#define JI_I2C_SDAHD(_n1) (_n1)
+
+#define REG_I2C_ABTSRC(_n1) jz_reg(I2C_ABTSRC(_n1))
+#define JA_I2C_ABTSRC(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x80)
+#define JT_I2C_ABTSRC(_n1) JIO_32_RW
+#define JN_I2C_ABTSRC(_n1) I2C_ABTSRC
+#define JI_I2C_ABTSRC(_n1) (_n1)
+
+#define REG_I2C_DMACR(_n1) jz_reg(I2C_DMACR(_n1))
+#define JA_I2C_DMACR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x88)
+#define JT_I2C_DMACR(_n1) JIO_32_RW
+#define JN_I2C_DMACR(_n1) I2C_DMACR
+#define JI_I2C_DMACR(_n1) (_n1)
+
+#define REG_I2C_DMATDLR(_n1) jz_reg(I2C_DMATDLR(_n1))
+#define JA_I2C_DMATDLR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x8c)
+#define JT_I2C_DMATDLR(_n1) JIO_32_RW
+#define JN_I2C_DMATDLR(_n1) I2C_DMATDLR
+#define JI_I2C_DMATDLR(_n1) (_n1)
+
+#define REG_I2C_DMARDLR(_n1) jz_reg(I2C_DMARDLR(_n1))
+#define JA_I2C_DMARDLR(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x90)
+#define JT_I2C_DMARDLR(_n1) JIO_32_RW
+#define JN_I2C_DMARDLR(_n1) I2C_DMARDLR
+#define JI_I2C_DMARDLR(_n1) (_n1)
+
+#define REG_I2C_SDASU(_n1) jz_reg(I2C_SDASU(_n1))
+#define JA_I2C_SDASU(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x94)
+#define JT_I2C_SDASU(_n1) JIO_32_RW
+#define JN_I2C_SDASU(_n1) I2C_SDASU
+#define JI_I2C_SDASU(_n1) (_n1)
+
+#define REG_I2C_ACKGC(_n1) jz_reg(I2C_ACKGC(_n1))
+#define JA_I2C_ACKGC(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x98)
+#define JT_I2C_ACKGC(_n1) JIO_32_RW
+#define JN_I2C_ACKGC(_n1) I2C_ACKGC
+#define JI_I2C_ACKGC(_n1) (_n1)
+
+#define REG_I2C_FLT(_n1) jz_reg(I2C_FLT(_n1))
+#define JA_I2C_FLT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0xa0)
+#define JT_I2C_FLT(_n1) JIO_32_RW
+#define JN_I2C_FLT(_n1) I2C_FLT
+#define JI_I2C_FLT(_n1) (_n1)
+
+#define REG_I2C_CINT(_n1) jz_reg(I2C_CINT(_n1))
+#define JA_I2C_CINT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x40)
+#define JT_I2C_CINT(_n1) JIO_32_RW
+#define JN_I2C_CINT(_n1) I2C_CINT
+#define JI_I2C_CINT(_n1) (_n1)
+
+#define REG_I2C_CRXUF(_n1) jz_reg(I2C_CRXUF(_n1))
+#define JA_I2C_CRXUF(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x44)
+#define JT_I2C_CRXUF(_n1) JIO_32_RW
+#define JN_I2C_CRXUF(_n1) I2C_CRXUF
+#define JI_I2C_CRXUF(_n1) (_n1)
+
+#define REG_I2C_CRXOF(_n1) jz_reg(I2C_CRXOF(_n1))
+#define JA_I2C_CRXOF(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x48)
+#define JT_I2C_CRXOF(_n1) JIO_32_RW
+#define JN_I2C_CRXOF(_n1) I2C_CRXOF
+#define JI_I2C_CRXOF(_n1) (_n1)
+
+#define REG_I2C_CTXOF(_n1) jz_reg(I2C_CTXOF(_n1))
+#define JA_I2C_CTXOF(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x4c)
+#define JT_I2C_CTXOF(_n1) JIO_32_RW
+#define JN_I2C_CTXOF(_n1) I2C_CTXOF
+#define JI_I2C_CTXOF(_n1) (_n1)
+
+#define REG_I2C_CRXREQ(_n1) jz_reg(I2C_CRXREQ(_n1))
+#define JA_I2C_CRXREQ(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x50)
+#define JT_I2C_CRXREQ(_n1) JIO_32_RW
+#define JN_I2C_CRXREQ(_n1) I2C_CRXREQ
+#define JI_I2C_CRXREQ(_n1) (_n1)
+
+#define REG_I2C_CTXABT(_n1) jz_reg(I2C_CTXABT(_n1))
+#define JA_I2C_CTXABT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x54)
+#define JT_I2C_CTXABT(_n1) JIO_32_RW
+#define JN_I2C_CTXABT(_n1) I2C_CTXABT
+#define JI_I2C_CTXABT(_n1) (_n1)
+
+#define REG_I2C_CRXDN(_n1) jz_reg(I2C_CRXDN(_n1))
+#define JA_I2C_CRXDN(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x58)
+#define JT_I2C_CRXDN(_n1) JIO_32_RW
+#define JN_I2C_CRXDN(_n1) I2C_CRXDN
+#define JI_I2C_CRXDN(_n1) (_n1)
+
+#define REG_I2C_CACT(_n1) jz_reg(I2C_CACT(_n1))
+#define JA_I2C_CACT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x5c)
+#define JT_I2C_CACT(_n1) JIO_32_RW
+#define JN_I2C_CACT(_n1) I2C_CACT
+#define JI_I2C_CACT(_n1) (_n1)
+
+#define REG_I2C_CSTP(_n1) jz_reg(I2C_CSTP(_n1))
+#define JA_I2C_CSTP(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x60)
+#define JT_I2C_CSTP(_n1) JIO_32_RW
+#define JN_I2C_CSTP(_n1) I2C_CSTP
+#define JI_I2C_CSTP(_n1) (_n1)
+
+#define REG_I2C_CSTT(_n1) jz_reg(I2C_CSTT(_n1))
+#define JA_I2C_CSTT(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x64)
+#define JT_I2C_CSTT(_n1) JIO_32_RW
+#define JN_I2C_CSTT(_n1) I2C_CSTT
+#define JI_I2C_CSTT(_n1) (_n1)
+
+#define REG_I2C_CGC(_n1) jz_reg(I2C_CGC(_n1))
+#define JA_I2C_CGC(_n1) (0xb0050000 + (_n1) * 0x1000 + 0x68)
+#define JT_I2C_CGC(_n1) JIO_32_RW
+#define JN_I2C_CGC(_n1) I2C_CGC
+#define JI_I2C_CGC(_n1) (_n1)
+
+#endif /* __HEADERGEN_I2C_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/intc.h b/firmware/target/mips/ingenic_x1000/x1000/intc.h
new file mode 100644
index 0000000000..37fbf33fb6
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/intc.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_INTC_H__
+#define __HEADERGEN_INTC_H__
+
+#include "macro.h"
+
+#define REG_INTC_SRC(_n1) jz_reg(INTC_SRC(_n1))
+#define JA_INTC_SRC(_n1) (0xb0001000 + 0x0 + (_n1) * 0x20)
+#define JT_INTC_SRC(_n1) JIO_32_RW
+#define JN_INTC_SRC(_n1) INTC_SRC
+#define JI_INTC_SRC(_n1) (_n1)
+
+#define REG_INTC_MSK(_n1) jz_reg(INTC_MSK(_n1))
+#define JA_INTC_MSK(_n1) (0xb0001000 + 0x4 + (_n1) * 0x20)
+#define JT_INTC_MSK(_n1) JIO_32_RW
+#define JN_INTC_MSK(_n1) INTC_MSK
+#define JI_INTC_MSK(_n1) (_n1)
+#define REG_INTC_MSK_SET(_n1) jz_reg(INTC_MSK_SET(_n1))
+#define JA_INTC_MSK_SET(_n1) (JA_INTC_MSK(_n1) + 0x4)
+#define JT_INTC_MSK_SET(_n1) JIO_32_WO
+#define JN_INTC_MSK_SET(_n1) INTC_MSK
+#define JI_INTC_MSK_SET(_n1) (_n1)
+#define REG_INTC_MSK_CLR(_n1) jz_reg(INTC_MSK_CLR(_n1))
+#define JA_INTC_MSK_CLR(_n1) (JA_INTC_MSK(_n1) + 0x8)
+#define JT_INTC_MSK_CLR(_n1) JIO_32_WO
+#define JN_INTC_MSK_CLR(_n1) INTC_MSK
+#define JI_INTC_MSK_CLR(_n1) (_n1)
+
+#define REG_INTC_PND(_n1) jz_reg(INTC_PND(_n1))
+#define JA_INTC_PND(_n1) (0xb0001000 + 0x10 + (_n1) * 0x20)
+#define JT_INTC_PND(_n1) JIO_32_RW
+#define JN_INTC_PND(_n1) INTC_PND
+#define JI_INTC_PND(_n1) (_n1)
+
+#endif /* __HEADERGEN_INTC_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/lcd.h b/firmware/target/mips/ingenic_x1000/x1000/lcd.h
new file mode 100644
index 0000000000..d4c1fe1878
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/lcd.h
@@ -0,0 +1,446 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_LCD_H__
+#define __HEADERGEN_LCD_H__
+
+#include "macro.h"
+
+#define REG_LCD_CFG jz_reg(LCD_CFG)
+#define JA_LCD_CFG (0xb3050000 + 0x0)
+#define JT_LCD_CFG JIO_32_RW
+#define JN_LCD_CFG LCD_CFG
+#define JI_LCD_CFG
+#define BP_LCD_CFG_INVDAT 17
+#define BM_LCD_CFG_INVDAT 0x20000
+#define BF_LCD_CFG_INVDAT(v) (((v) & 0x1) << 17)
+#define BFM_LCD_CFG_INVDAT(v) BM_LCD_CFG_INVDAT
+#define BF_LCD_CFG_INVDAT_V(e) BF_LCD_CFG_INVDAT(BV_LCD_CFG_INVDAT__##e)
+#define BFM_LCD_CFG_INVDAT_V(v) BM_LCD_CFG_INVDAT
+
+#define REG_LCD_CTRL jz_reg(LCD_CTRL)
+#define JA_LCD_CTRL (0xb3050000 + 0x30)
+#define JT_LCD_CTRL JIO_32_RW
+#define JN_LCD_CTRL LCD_CTRL
+#define JI_LCD_CTRL
+#define BP_LCD_CTRL_BURST 28
+#define BM_LCD_CTRL_BURST 0x70000000
+#define BV_LCD_CTRL_BURST__4WORD 0x0
+#define BV_LCD_CTRL_BURST__8WORD 0x1
+#define BV_LCD_CTRL_BURST__16WORD 0x2
+#define BV_LCD_CTRL_BURST__32WORD 0x3
+#define BV_LCD_CTRL_BURST__64WORD 0x4
+#define BF_LCD_CTRL_BURST(v) (((v) & 0x7) << 28)
+#define BFM_LCD_CTRL_BURST(v) BM_LCD_CTRL_BURST
+#define BF_LCD_CTRL_BURST_V(e) BF_LCD_CTRL_BURST(BV_LCD_CTRL_BURST__##e)
+#define BFM_LCD_CTRL_BURST_V(v) BM_LCD_CTRL_BURST
+#define BP_LCD_CTRL_BPP 0
+#define BM_LCD_CTRL_BPP 0x7
+#define BV_LCD_CTRL_BPP__15BIT_OR_16BIT 0x4
+#define BV_LCD_CTRL_BPP__18BIT_OR_24BIT 0x5
+#define BV_LCD_CTRL_BPP__24BIT_COMPRESSED 0x6
+#define BV_LCD_CTRL_BPP__30BIT 0x7
+#define BF_LCD_CTRL_BPP(v) (((v) & 0x7) << 0)
+#define BFM_LCD_CTRL_BPP(v) BM_LCD_CTRL_BPP
+#define BF_LCD_CTRL_BPP_V(e) BF_LCD_CTRL_BPP(BV_LCD_CTRL_BPP__##e)
+#define BFM_LCD_CTRL_BPP_V(v) BM_LCD_CTRL_BPP
+#define BP_LCD_CTRL_EOFM 13
+#define BM_LCD_CTRL_EOFM 0x2000
+#define BF_LCD_CTRL_EOFM(v) (((v) & 0x1) << 13)
+#define BFM_LCD_CTRL_EOFM(v) BM_LCD_CTRL_EOFM
+#define BF_LCD_CTRL_EOFM_V(e) BF_LCD_CTRL_EOFM(BV_LCD_CTRL_EOFM__##e)
+#define BFM_LCD_CTRL_EOFM_V(v) BM_LCD_CTRL_EOFM
+#define BP_LCD_CTRL_SOFM 12
+#define BM_LCD_CTRL_SOFM 0x1000
+#define BF_LCD_CTRL_SOFM(v) (((v) & 0x1) << 12)
+#define BFM_LCD_CTRL_SOFM(v) BM_LCD_CTRL_SOFM
+#define BF_LCD_CTRL_SOFM_V(e) BF_LCD_CTRL_SOFM(BV_LCD_CTRL_SOFM__##e)
+#define BFM_LCD_CTRL_SOFM_V(v) BM_LCD_CTRL_SOFM
+#define BP_LCD_CTRL_IFUM 10
+#define BM_LCD_CTRL_IFUM 0x400
+#define BF_LCD_CTRL_IFUM(v) (((v) & 0x1) << 10)
+#define BFM_LCD_CTRL_IFUM(v) BM_LCD_CTRL_IFUM
+#define BF_LCD_CTRL_IFUM_V(e) BF_LCD_CTRL_IFUM(BV_LCD_CTRL_IFUM__##e)
+#define BFM_LCD_CTRL_IFUM_V(v) BM_LCD_CTRL_IFUM
+#define BP_LCD_CTRL_QDM 7
+#define BM_LCD_CTRL_QDM 0x80
+#define BF_LCD_CTRL_QDM(v) (((v) & 0x1) << 7)
+#define BFM_LCD_CTRL_QDM(v) BM_LCD_CTRL_QDM
+#define BF_LCD_CTRL_QDM_V(e) BF_LCD_CTRL_QDM(BV_LCD_CTRL_QDM__##e)
+#define BFM_LCD_CTRL_QDM_V(v) BM_LCD_CTRL_QDM
+#define BP_LCD_CTRL_BEDN 6
+#define BM_LCD_CTRL_BEDN 0x40
+#define BF_LCD_CTRL_BEDN(v) (((v) & 0x1) << 6)
+#define BFM_LCD_CTRL_BEDN(v) BM_LCD_CTRL_BEDN
+#define BF_LCD_CTRL_BEDN_V(e) BF_LCD_CTRL_BEDN(BV_LCD_CTRL_BEDN__##e)
+#define BFM_LCD_CTRL_BEDN_V(v) BM_LCD_CTRL_BEDN
+#define BP_LCD_CTRL_PEDN 5
+#define BM_LCD_CTRL_PEDN 0x20
+#define BF_LCD_CTRL_PEDN(v) (((v) & 0x1) << 5)
+#define BFM_LCD_CTRL_PEDN(v) BM_LCD_CTRL_PEDN
+#define BF_LCD_CTRL_PEDN_V(e) BF_LCD_CTRL_PEDN(BV_LCD_CTRL_PEDN__##e)
+#define BFM_LCD_CTRL_PEDN_V(v) BM_LCD_CTRL_PEDN
+#define BP_LCD_CTRL_ENABLE 3
+#define BM_LCD_CTRL_ENABLE 0x8
+#define BF_LCD_CTRL_ENABLE(v) (((v) & 0x1) << 3)
+#define BFM_LCD_CTRL_ENABLE(v) BM_LCD_CTRL_ENABLE
+#define BF_LCD_CTRL_ENABLE_V(e) BF_LCD_CTRL_ENABLE(BV_LCD_CTRL_ENABLE__##e)
+#define BFM_LCD_CTRL_ENABLE_V(v) BM_LCD_CTRL_ENABLE
+
+#define REG_LCD_STATE jz_reg(LCD_STATE)
+#define JA_LCD_STATE (0xb3050000 + 0x34)
+#define JT_LCD_STATE JIO_32_RW
+#define JN_LCD_STATE LCD_STATE
+#define JI_LCD_STATE
+#define BP_LCD_STATE_QD 7
+#define BM_LCD_STATE_QD 0x80
+#define BF_LCD_STATE_QD(v) (((v) & 0x1) << 7)
+#define BFM_LCD_STATE_QD(v) BM_LCD_STATE_QD
+#define BF_LCD_STATE_QD_V(e) BF_LCD_STATE_QD(BV_LCD_STATE_QD__##e)
+#define BFM_LCD_STATE_QD_V(v) BM_LCD_STATE_QD
+#define BP_LCD_STATE_EOF 5
+#define BM_LCD_STATE_EOF 0x20
+#define BF_LCD_STATE_EOF(v) (((v) & 0x1) << 5)
+#define BFM_LCD_STATE_EOF(v) BM_LCD_STATE_EOF
+#define BF_LCD_STATE_EOF_V(e) BF_LCD_STATE_EOF(BV_LCD_STATE_EOF__##e)
+#define BFM_LCD_STATE_EOF_V(v) BM_LCD_STATE_EOF
+#define BP_LCD_STATE_SOF 4
+#define BM_LCD_STATE_SOF 0x10
+#define BF_LCD_STATE_SOF(v) (((v) & 0x1) << 4)
+#define BFM_LCD_STATE_SOF(v) BM_LCD_STATE_SOF
+#define BF_LCD_STATE_SOF_V(e) BF_LCD_STATE_SOF(BV_LCD_STATE_SOF__##e)
+#define BFM_LCD_STATE_SOF_V(v) BM_LCD_STATE_SOF
+#define BP_LCD_STATE_IFU 2
+#define BM_LCD_STATE_IFU 0x4
+#define BF_LCD_STATE_IFU(v) (((v) & 0x1) << 2)
+#define BFM_LCD_STATE_IFU(v) BM_LCD_STATE_IFU
+#define BF_LCD_STATE_IFU_V(e) BF_LCD_STATE_IFU(BV_LCD_STATE_IFU__##e)
+#define BFM_LCD_STATE_IFU_V(v) BM_LCD_STATE_IFU
+
+#define REG_LCD_OSDCTRL jz_reg(LCD_OSDCTRL)
+#define JA_LCD_OSDCTRL (0xb3050000 + 0x104)
+#define JT_LCD_OSDCTRL JIO_32_RW
+#define JN_LCD_OSDCTRL LCD_OSDCTRL
+#define JI_LCD_OSDCTRL
+
+#define REG_LCD_BGC jz_reg(LCD_BGC)
+#define JA_LCD_BGC (0xb3050000 + 0x10c)
+#define JT_LCD_BGC JIO_32_RW
+#define JN_LCD_BGC LCD_BGC
+#define JI_LCD_BGC
+
+#define REG_LCD_DAH jz_reg(LCD_DAH)
+#define JA_LCD_DAH (0xb3050000 + 0x10)
+#define JT_LCD_DAH JIO_32_RW
+#define JN_LCD_DAH LCD_DAH
+#define JI_LCD_DAH
+
+#define REG_LCD_DAV jz_reg(LCD_DAV)
+#define JA_LCD_DAV (0xb3050000 + 0x14)
+#define JT_LCD_DAV JIO_32_RW
+#define JN_LCD_DAV LCD_DAV
+#define JI_LCD_DAV
+
+#define REG_LCD_VAT jz_reg(LCD_VAT)
+#define JA_LCD_VAT (0xb3050000 + 0xc)
+#define JT_LCD_VAT JIO_32_RW
+#define JN_LCD_VAT LCD_VAT
+#define JI_LCD_VAT
+
+#define REG_LCD_VSYNC jz_reg(LCD_VSYNC)
+#define JA_LCD_VSYNC (0xb3050000 + 0x4)
+#define JT_LCD_VSYNC JIO_32_RW
+#define JN_LCD_VSYNC LCD_VSYNC
+#define JI_LCD_VSYNC
+
+#define REG_LCD_HSYNC jz_reg(LCD_HSYNC)
+#define JA_LCD_HSYNC (0xb3050000 + 0x8)
+#define JT_LCD_HSYNC JIO_32_RW
+#define JN_LCD_HSYNC LCD_HSYNC
+#define JI_LCD_HSYNC
+
+#define REG_LCD_IID jz_reg(LCD_IID)
+#define JA_LCD_IID (0xb3050000 + 0x38)
+#define JT_LCD_IID JIO_32_RW
+#define JN_LCD_IID LCD_IID
+#define JI_LCD_IID
+
+#define REG_LCD_DA jz_reg(LCD_DA)
+#define JA_LCD_DA (0xb3050000 + 0x40)
+#define JT_LCD_DA JIO_32_RW
+#define JN_LCD_DA LCD_DA
+#define JI_LCD_DA
+
+#define REG_LCD_MCFG jz_reg(LCD_MCFG)
+#define JA_LCD_MCFG (0xb3050000 + 0xa0)
+#define JT_LCD_MCFG JIO_32_RW
+#define JN_LCD_MCFG LCD_MCFG
+#define JI_LCD_MCFG
+#define BP_LCD_MCFG_CWIDTH 8
+#define BM_LCD_MCFG_CWIDTH 0x300
+#define BV_LCD_MCFG_CWIDTH__16BIT_OR_9BIT 0x0
+#define BV_LCD_MCFG_CWIDTH__8BIT 0x1
+#define BV_LCD_MCFG_CWIDTH__18BIT 0x2
+#define BV_LCD_MCFG_CWIDTH__24BIT 0x3
+#define BF_LCD_MCFG_CWIDTH(v) (((v) & 0x3) << 8)
+#define BFM_LCD_MCFG_CWIDTH(v) BM_LCD_MCFG_CWIDTH
+#define BF_LCD_MCFG_CWIDTH_V(e) BF_LCD_MCFG_CWIDTH(BV_LCD_MCFG_CWIDTH__##e)
+#define BFM_LCD_MCFG_CWIDTH_V(v) BM_LCD_MCFG_CWIDTH
+
+#define REG_LCD_MCFG_NEW jz_reg(LCD_MCFG_NEW)
+#define JA_LCD_MCFG_NEW (0xb3050000 + 0xb8)
+#define JT_LCD_MCFG_NEW JIO_32_RW
+#define JN_LCD_MCFG_NEW LCD_MCFG_NEW
+#define JI_LCD_MCFG_NEW
+#define BP_LCD_MCFG_NEW_DWIDTH 13
+#define BM_LCD_MCFG_NEW_DWIDTH 0xe000
+#define BV_LCD_MCFG_NEW_DWIDTH__8BIT 0x0
+#define BV_LCD_MCFG_NEW_DWIDTH__9BIT 0x1
+#define BV_LCD_MCFG_NEW_DWIDTH__16BIT 0x2
+#define BV_LCD_MCFG_NEW_DWIDTH__18BIT 0x3
+#define BV_LCD_MCFG_NEW_DWIDTH__24BIT 0x4
+#define BF_LCD_MCFG_NEW_DWIDTH(v) (((v) & 0x7) << 13)
+#define BFM_LCD_MCFG_NEW_DWIDTH(v) BM_LCD_MCFG_NEW_DWIDTH
+#define BF_LCD_MCFG_NEW_DWIDTH_V(e) BF_LCD_MCFG_NEW_DWIDTH(BV_LCD_MCFG_NEW_DWIDTH__##e)
+#define BFM_LCD_MCFG_NEW_DWIDTH_V(v) BM_LCD_MCFG_NEW_DWIDTH
+#define BP_LCD_MCFG_NEW_DTIMES 8
+#define BM_LCD_MCFG_NEW_DTIMES 0x300
+#define BV_LCD_MCFG_NEW_DTIMES__1TIME 0x0
+#define BV_LCD_MCFG_NEW_DTIMES__2TIME 0x1
+#define BV_LCD_MCFG_NEW_DTIMES__3TIME 0x2
+#define BF_LCD_MCFG_NEW_DTIMES(v) (((v) & 0x3) << 8)
+#define BFM_LCD_MCFG_NEW_DTIMES(v) BM_LCD_MCFG_NEW_DTIMES
+#define BF_LCD_MCFG_NEW_DTIMES_V(e) BF_LCD_MCFG_NEW_DTIMES(BV_LCD_MCFG_NEW_DTIMES__##e)
+#define BFM_LCD_MCFG_NEW_DTIMES_V(v) BM_LCD_MCFG_NEW_DTIMES
+#define BP_LCD_MCFG_NEW_6800_MODE 11
+#define BM_LCD_MCFG_NEW_6800_MODE 0x800
+#define BF_LCD_MCFG_NEW_6800_MODE(v) (((v) & 0x1) << 11)
+#define BFM_LCD_MCFG_NEW_6800_MODE(v) BM_LCD_MCFG_NEW_6800_MODE
+#define BF_LCD_MCFG_NEW_6800_MODE_V(e) BF_LCD_MCFG_NEW_6800_MODE(BV_LCD_MCFG_NEW_6800_MODE__##e)
+#define BFM_LCD_MCFG_NEW_6800_MODE_V(v) BM_LCD_MCFG_NEW_6800_MODE
+#define BP_LCD_MCFG_NEW_CMD_9BIT 10
+#define BM_LCD_MCFG_NEW_CMD_9BIT 0x400
+#define BF_LCD_MCFG_NEW_CMD_9BIT(v) (((v) & 0x1) << 10)
+#define BFM_LCD_MCFG_NEW_CMD_9BIT(v) BM_LCD_MCFG_NEW_CMD_9BIT
+#define BF_LCD_MCFG_NEW_CMD_9BIT_V(e) BF_LCD_MCFG_NEW_CMD_9BIT(BV_LCD_MCFG_NEW_CMD_9BIT__##e)
+#define BFM_LCD_MCFG_NEW_CMD_9BIT_V(v) BM_LCD_MCFG_NEW_CMD_9BIT
+#define BP_LCD_MCFG_NEW_CSPLY 5
+#define BM_LCD_MCFG_NEW_CSPLY 0x20
+#define BF_LCD_MCFG_NEW_CSPLY(v) (((v) & 0x1) << 5)
+#define BFM_LCD_MCFG_NEW_CSPLY(v) BM_LCD_MCFG_NEW_CSPLY
+#define BF_LCD_MCFG_NEW_CSPLY_V(e) BF_LCD_MCFG_NEW_CSPLY(BV_LCD_MCFG_NEW_CSPLY__##e)
+#define BFM_LCD_MCFG_NEW_CSPLY_V(v) BM_LCD_MCFG_NEW_CSPLY
+#define BP_LCD_MCFG_NEW_RSPLY 4
+#define BM_LCD_MCFG_NEW_RSPLY 0x10
+#define BF_LCD_MCFG_NEW_RSPLY(v) (((v) & 0x1) << 4)
+#define BFM_LCD_MCFG_NEW_RSPLY(v) BM_LCD_MCFG_NEW_RSPLY
+#define BF_LCD_MCFG_NEW_RSPLY_V(e) BF_LCD_MCFG_NEW_RSPLY(BV_LCD_MCFG_NEW_RSPLY__##e)
+#define BFM_LCD_MCFG_NEW_RSPLY_V(v) BM_LCD_MCFG_NEW_RSPLY
+#define BP_LCD_MCFG_NEW_CLKPLY 3
+#define BM_LCD_MCFG_NEW_CLKPLY 0x8
+#define BF_LCD_MCFG_NEW_CLKPLY(v) (((v) & 0x1) << 3)
+#define BFM_LCD_MCFG_NEW_CLKPLY(v) BM_LCD_MCFG_NEW_CLKPLY
+#define BF_LCD_MCFG_NEW_CLKPLY_V(e) BF_LCD_MCFG_NEW_CLKPLY(BV_LCD_MCFG_NEW_CLKPLY__##e)
+#define BFM_LCD_MCFG_NEW_CLKPLY_V(v) BM_LCD_MCFG_NEW_CLKPLY
+#define BP_LCD_MCFG_NEW_DTYPE 2
+#define BM_LCD_MCFG_NEW_DTYPE 0x4
+#define BV_LCD_MCFG_NEW_DTYPE__SERIAL 0x1
+#define BV_LCD_MCFG_NEW_DTYPE__PARALLEL 0x0
+#define BF_LCD_MCFG_NEW_DTYPE(v) (((v) & 0x1) << 2)
+#define BFM_LCD_MCFG_NEW_DTYPE(v) BM_LCD_MCFG_NEW_DTYPE
+#define BF_LCD_MCFG_NEW_DTYPE_V(e) BF_LCD_MCFG_NEW_DTYPE(BV_LCD_MCFG_NEW_DTYPE__##e)
+#define BFM_LCD_MCFG_NEW_DTYPE_V(v) BM_LCD_MCFG_NEW_DTYPE
+#define BP_LCD_MCFG_NEW_CTYPE 1
+#define BM_LCD_MCFG_NEW_CTYPE 0x2
+#define BV_LCD_MCFG_NEW_CTYPE__SERIAL 0x1
+#define BV_LCD_MCFG_NEW_CTYPE__PARALLEL 0x0
+#define BF_LCD_MCFG_NEW_CTYPE(v) (((v) & 0x1) << 1)
+#define BFM_LCD_MCFG_NEW_CTYPE(v) BM_LCD_MCFG_NEW_CTYPE
+#define BF_LCD_MCFG_NEW_CTYPE_V(e) BF_LCD_MCFG_NEW_CTYPE(BV_LCD_MCFG_NEW_CTYPE__##e)
+#define BFM_LCD_MCFG_NEW_CTYPE_V(v) BM_LCD_MCFG_NEW_CTYPE
+#define BP_LCD_MCFG_NEW_FMT_CONV 0
+#define BM_LCD_MCFG_NEW_FMT_CONV 0x1
+#define BF_LCD_MCFG_NEW_FMT_CONV(v) (((v) & 0x1) << 0)
+#define BFM_LCD_MCFG_NEW_FMT_CONV(v) BM_LCD_MCFG_NEW_FMT_CONV
+#define BF_LCD_MCFG_NEW_FMT_CONV_V(e) BF_LCD_MCFG_NEW_FMT_CONV(BV_LCD_MCFG_NEW_FMT_CONV__##e)
+#define BFM_LCD_MCFG_NEW_FMT_CONV_V(v) BM_LCD_MCFG_NEW_FMT_CONV
+
+#define REG_LCD_MCTRL jz_reg(LCD_MCTRL)
+#define JA_LCD_MCTRL (0xb3050000 + 0xa4)
+#define JT_LCD_MCTRL JIO_32_RW
+#define JN_LCD_MCTRL LCD_MCTRL
+#define JI_LCD_MCTRL
+#define BP_LCD_MCTRL_NARROW_TE 10
+#define BM_LCD_MCTRL_NARROW_TE 0x400
+#define BF_LCD_MCTRL_NARROW_TE(v) (((v) & 0x1) << 10)
+#define BFM_LCD_MCTRL_NARROW_TE(v) BM_LCD_MCTRL_NARROW_TE
+#define BF_LCD_MCTRL_NARROW_TE_V(e) BF_LCD_MCTRL_NARROW_TE(BV_LCD_MCTRL_NARROW_TE__##e)
+#define BFM_LCD_MCTRL_NARROW_TE_V(v) BM_LCD_MCTRL_NARROW_TE
+#define BP_LCD_MCTRL_TE_INV 9
+#define BM_LCD_MCTRL_TE_INV 0x200
+#define BF_LCD_MCTRL_TE_INV(v) (((v) & 0x1) << 9)
+#define BFM_LCD_MCTRL_TE_INV(v) BM_LCD_MCTRL_TE_INV
+#define BF_LCD_MCTRL_TE_INV_V(e) BF_LCD_MCTRL_TE_INV(BV_LCD_MCTRL_TE_INV__##e)
+#define BFM_LCD_MCTRL_TE_INV_V(v) BM_LCD_MCTRL_TE_INV
+#define BP_LCD_MCTRL_NOT_USE_TE 8
+#define BM_LCD_MCTRL_NOT_USE_TE 0x100
+#define BF_LCD_MCTRL_NOT_USE_TE(v) (((v) & 0x1) << 8)
+#define BFM_LCD_MCTRL_NOT_USE_TE(v) BM_LCD_MCTRL_NOT_USE_TE
+#define BF_LCD_MCTRL_NOT_USE_TE_V(e) BF_LCD_MCTRL_NOT_USE_TE(BV_LCD_MCTRL_NOT_USE_TE__##e)
+#define BFM_LCD_MCTRL_NOT_USE_TE_V(v) BM_LCD_MCTRL_NOT_USE_TE
+#define BP_LCD_MCTRL_DCSI_SEL 7
+#define BM_LCD_MCTRL_DCSI_SEL 0x80
+#define BF_LCD_MCTRL_DCSI_SEL(v) (((v) & 0x1) << 7)
+#define BFM_LCD_MCTRL_DCSI_SEL(v) BM_LCD_MCTRL_DCSI_SEL
+#define BF_LCD_MCTRL_DCSI_SEL_V(e) BF_LCD_MCTRL_DCSI_SEL(BV_LCD_MCTRL_DCSI_SEL__##e)
+#define BFM_LCD_MCTRL_DCSI_SEL_V(v) BM_LCD_MCTRL_DCSI_SEL
+#define BP_LCD_MCTRL_MIPI_SLCD 6
+#define BM_LCD_MCTRL_MIPI_SLCD 0x40
+#define BF_LCD_MCTRL_MIPI_SLCD(v) (((v) & 0x1) << 6)
+#define BFM_LCD_MCTRL_MIPI_SLCD(v) BM_LCD_MCTRL_MIPI_SLCD
+#define BF_LCD_MCTRL_MIPI_SLCD_V(e) BF_LCD_MCTRL_MIPI_SLCD(BV_LCD_MCTRL_MIPI_SLCD__##e)
+#define BFM_LCD_MCTRL_MIPI_SLCD_V(v) BM_LCD_MCTRL_MIPI_SLCD
+#define BP_LCD_MCTRL_FAST_MODE 4
+#define BM_LCD_MCTRL_FAST_MODE 0x10
+#define BF_LCD_MCTRL_FAST_MODE(v) (((v) & 0x1) << 4)
+#define BFM_LCD_MCTRL_FAST_MODE(v) BM_LCD_MCTRL_FAST_MODE
+#define BF_LCD_MCTRL_FAST_MODE_V(e) BF_LCD_MCTRL_FAST_MODE(BV_LCD_MCTRL_FAST_MODE__##e)
+#define BFM_LCD_MCTRL_FAST_MODE_V(v) BM_LCD_MCTRL_FAST_MODE
+#define BP_LCD_MCTRL_GATE_MASK 3
+#define BM_LCD_MCTRL_GATE_MASK 0x8
+#define BF_LCD_MCTRL_GATE_MASK(v) (((v) & 0x1) << 3)
+#define BFM_LCD_MCTRL_GATE_MASK(v) BM_LCD_MCTRL_GATE_MASK
+#define BF_LCD_MCTRL_GATE_MASK_V(e) BF_LCD_MCTRL_GATE_MASK(BV_LCD_MCTRL_GATE_MASK__##e)
+#define BFM_LCD_MCTRL_GATE_MASK_V(v) BM_LCD_MCTRL_GATE_MASK
+#define BP_LCD_MCTRL_DMA_MODE 2
+#define BM_LCD_MCTRL_DMA_MODE 0x4
+#define BF_LCD_MCTRL_DMA_MODE(v) (((v) & 0x1) << 2)
+#define BFM_LCD_MCTRL_DMA_MODE(v) BM_LCD_MCTRL_DMA_MODE
+#define BF_LCD_MCTRL_DMA_MODE_V(e) BF_LCD_MCTRL_DMA_MODE(BV_LCD_MCTRL_DMA_MODE__##e)
+#define BFM_LCD_MCTRL_DMA_MODE_V(v) BM_LCD_MCTRL_DMA_MODE
+#define BP_LCD_MCTRL_DMA_START 1
+#define BM_LCD_MCTRL_DMA_START 0x2
+#define BF_LCD_MCTRL_DMA_START(v) (((v) & 0x1) << 1)
+#define BFM_LCD_MCTRL_DMA_START(v) BM_LCD_MCTRL_DMA_START
+#define BF_LCD_MCTRL_DMA_START_V(e) BF_LCD_MCTRL_DMA_START(BV_LCD_MCTRL_DMA_START__##e)
+#define BFM_LCD_MCTRL_DMA_START_V(v) BM_LCD_MCTRL_DMA_START
+#define BP_LCD_MCTRL_DMA_TX_EN 0
+#define BM_LCD_MCTRL_DMA_TX_EN 0x1
+#define BF_LCD_MCTRL_DMA_TX_EN(v) (((v) & 0x1) << 0)
+#define BFM_LCD_MCTRL_DMA_TX_EN(v) BM_LCD_MCTRL_DMA_TX_EN
+#define BF_LCD_MCTRL_DMA_TX_EN_V(e) BF_LCD_MCTRL_DMA_TX_EN(BV_LCD_MCTRL_DMA_TX_EN__##e)
+#define BFM_LCD_MCTRL_DMA_TX_EN_V(v) BM_LCD_MCTRL_DMA_TX_EN
+
+#define REG_LCD_MSTATE jz_reg(LCD_MSTATE)
+#define JA_LCD_MSTATE (0xb3050000 + 0xa8)
+#define JT_LCD_MSTATE JIO_32_RW
+#define JN_LCD_MSTATE LCD_MSTATE
+#define JI_LCD_MSTATE
+#define BP_LCD_MSTATE_LCD_ID 16
+#define BM_LCD_MSTATE_LCD_ID 0xffff0000
+#define BF_LCD_MSTATE_LCD_ID(v) (((v) & 0xffff) << 16)
+#define BFM_LCD_MSTATE_LCD_ID(v) BM_LCD_MSTATE_LCD_ID
+#define BF_LCD_MSTATE_LCD_ID_V(e) BF_LCD_MSTATE_LCD_ID(BV_LCD_MSTATE_LCD_ID__##e)
+#define BFM_LCD_MSTATE_LCD_ID_V(v) BM_LCD_MSTATE_LCD_ID
+#define BP_LCD_MSTATE_BUSY 0
+#define BM_LCD_MSTATE_BUSY 0x1
+#define BF_LCD_MSTATE_BUSY(v) (((v) & 0x1) << 0)
+#define BFM_LCD_MSTATE_BUSY(v) BM_LCD_MSTATE_BUSY
+#define BF_LCD_MSTATE_BUSY_V(e) BF_LCD_MSTATE_BUSY(BV_LCD_MSTATE_BUSY__##e)
+#define BFM_LCD_MSTATE_BUSY_V(v) BM_LCD_MSTATE_BUSY
+
+#define REG_LCD_MDATA jz_reg(LCD_MDATA)
+#define JA_LCD_MDATA (0xb3050000 + 0xac)
+#define JT_LCD_MDATA JIO_32_RW
+#define JN_LCD_MDATA LCD_MDATA
+#define JI_LCD_MDATA
+#define BP_LCD_MDATA_TYPE 30
+#define BM_LCD_MDATA_TYPE 0xc0000000
+#define BV_LCD_MDATA_TYPE__CMD 0x1
+#define BV_LCD_MDATA_TYPE__DAT 0x0
+#define BF_LCD_MDATA_TYPE(v) (((v) & 0x3) << 30)
+#define BFM_LCD_MDATA_TYPE(v) BM_LCD_MDATA_TYPE
+#define BF_LCD_MDATA_TYPE_V(e) BF_LCD_MDATA_TYPE(BV_LCD_MDATA_TYPE__##e)
+#define BFM_LCD_MDATA_TYPE_V(v) BM_LCD_MDATA_TYPE
+#define BP_LCD_MDATA_DATA 0
+#define BM_LCD_MDATA_DATA 0xffffff
+#define BF_LCD_MDATA_DATA(v) (((v) & 0xffffff) << 0)
+#define BFM_LCD_MDATA_DATA(v) BM_LCD_MDATA_DATA
+#define BF_LCD_MDATA_DATA_V(e) BF_LCD_MDATA_DATA(BV_LCD_MDATA_DATA__##e)
+#define BFM_LCD_MDATA_DATA_V(v) BM_LCD_MDATA_DATA
+
+#define REG_LCD_WTIME jz_reg(LCD_WTIME)
+#define JA_LCD_WTIME (0xb3050000 + 0xb0)
+#define JT_LCD_WTIME JIO_32_RW
+#define JN_LCD_WTIME LCD_WTIME
+#define JI_LCD_WTIME
+#define BP_LCD_WTIME_DHTIME 24
+#define BM_LCD_WTIME_DHTIME 0xff000000
+#define BF_LCD_WTIME_DHTIME(v) (((v) & 0xff) << 24)
+#define BFM_LCD_WTIME_DHTIME(v) BM_LCD_WTIME_DHTIME
+#define BF_LCD_WTIME_DHTIME_V(e) BF_LCD_WTIME_DHTIME(BV_LCD_WTIME_DHTIME__##e)
+#define BFM_LCD_WTIME_DHTIME_V(v) BM_LCD_WTIME_DHTIME
+#define BP_LCD_WTIME_DLTIME 16
+#define BM_LCD_WTIME_DLTIME 0xff0000
+#define BF_LCD_WTIME_DLTIME(v) (((v) & 0xff) << 16)
+#define BFM_LCD_WTIME_DLTIME(v) BM_LCD_WTIME_DLTIME
+#define BF_LCD_WTIME_DLTIME_V(e) BF_LCD_WTIME_DLTIME(BV_LCD_WTIME_DLTIME__##e)
+#define BFM_LCD_WTIME_DLTIME_V(v) BM_LCD_WTIME_DLTIME
+#define BP_LCD_WTIME_CHTIME 8
+#define BM_LCD_WTIME_CHTIME 0xff00
+#define BF_LCD_WTIME_CHTIME(v) (((v) & 0xff) << 8)
+#define BFM_LCD_WTIME_CHTIME(v) BM_LCD_WTIME_CHTIME
+#define BF_LCD_WTIME_CHTIME_V(e) BF_LCD_WTIME_CHTIME(BV_LCD_WTIME_CHTIME__##e)
+#define BFM_LCD_WTIME_CHTIME_V(v) BM_LCD_WTIME_CHTIME
+#define BP_LCD_WTIME_CLTIME 0
+#define BM_LCD_WTIME_CLTIME 0xff
+#define BF_LCD_WTIME_CLTIME(v) (((v) & 0xff) << 0)
+#define BFM_LCD_WTIME_CLTIME(v) BM_LCD_WTIME_CLTIME
+#define BF_LCD_WTIME_CLTIME_V(e) BF_LCD_WTIME_CLTIME(BV_LCD_WTIME_CLTIME__##e)
+#define BFM_LCD_WTIME_CLTIME_V(v) BM_LCD_WTIME_CLTIME
+
+#define REG_LCD_TASH jz_reg(LCD_TASH)
+#define JA_LCD_TASH (0xb3050000 + 0xb4)
+#define JT_LCD_TASH JIO_32_RW
+#define JN_LCD_TASH LCD_TASH
+#define JI_LCD_TASH
+#define BP_LCD_TASH_TAH 8
+#define BM_LCD_TASH_TAH 0xff00
+#define BF_LCD_TASH_TAH(v) (((v) & 0xff) << 8)
+#define BFM_LCD_TASH_TAH(v) BM_LCD_TASH_TAH
+#define BF_LCD_TASH_TAH_V(e) BF_LCD_TASH_TAH(BV_LCD_TASH_TAH__##e)
+#define BFM_LCD_TASH_TAH_V(v) BM_LCD_TASH_TAH
+#define BP_LCD_TASH_TAS 0
+#define BM_LCD_TASH_TAS 0xff
+#define BF_LCD_TASH_TAS(v) (((v) & 0xff) << 0)
+#define BFM_LCD_TASH_TAS(v) BM_LCD_TASH_TAS
+#define BF_LCD_TASH_TAS_V(e) BF_LCD_TASH_TAS(BV_LCD_TASH_TAS__##e)
+#define BFM_LCD_TASH_TAS_V(v) BM_LCD_TASH_TAS
+
+#define REG_LCD_SMWT jz_reg(LCD_SMWT)
+#define JA_LCD_SMWT (0xb3050000 + 0xbc)
+#define JT_LCD_SMWT JIO_32_RW
+#define JN_LCD_SMWT LCD_SMWT
+#define JI_LCD_SMWT
+
+#endif /* __HEADERGEN_LCD_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/macro.h b/firmware/target/mips/ingenic_x1000/x1000/macro.h
new file mode 100644
index 0000000000..bfe8708a91
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/macro.h
@@ -0,0 +1,356 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_MACRO_H__
+#define __HEADERGEN_MACRO_H__
+
+#include <stdint.h>
+
+#define __VAR_OR1(prefix, suffix) \
+ (prefix##suffix)
+#define __VAR_OR2(pre, s1, s2) \
+ (__VAR_OR1(pre, s1) | __VAR_OR1(pre, s2))
+#define __VAR_OR3(pre, s1, s2, s3) \
+ (__VAR_OR1(pre, s1) | __VAR_OR2(pre, s2, s3))
+#define __VAR_OR4(pre, s1, s2, s3, s4) \
+ (__VAR_OR2(pre, s1, s2) | __VAR_OR2(pre, s3, s4))
+#define __VAR_OR5(pre, s1, s2, s3, s4, s5) \
+ (__VAR_OR2(pre, s1, s2) | __VAR_OR3(pre, s3, s4, s5))
+#define __VAR_OR6(pre, s1, s2, s3, s4, s5, s6) \
+ (__VAR_OR3(pre, s1, s2, s3) | __VAR_OR3(pre, s4, s5, s6))
+#define __VAR_OR7(pre, s1, s2, s3, s4, s5, s6, s7) \
+ (__VAR_OR3(pre, s1, s2, s3) | __VAR_OR4(pre, s4, s5, s6, s7))
+#define __VAR_OR8(pre, s1, s2, s3, s4, s5, s6, s7, s8) \
+ (__VAR_OR4(pre, s1, s2, s3, s4) | __VAR_OR4(pre, s5, s6, s7, s8))
+#define __VAR_OR9(pre, s1, s2, s3, s4, s5, s6, s7, s8, s9) \
+ (__VAR_OR4(pre, s1, s2, s3, s4) | __VAR_OR5(pre, s5, s6, s7, s8, s9))
+#define __VAR_OR10(pre, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \
+ (__VAR_OR5(pre, s1, s2, s3, s4, s5) | __VAR_OR5(pre, s6, s7, s8, s9, s10))
+#define __VAR_OR11(pre, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11) \
+ (__VAR_OR5(pre, s1, s2, s3, s4, s5) | __VAR_OR6(pre, s6, s7, s8, s9, s10, s11))
+#define __VAR_OR12(pre, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12) \
+ (__VAR_OR6(pre, s1, s2, s3, s4, s5, s6) | __VAR_OR6(pre, s7, s8, s9, s10, s11, s12))
+#define __VAR_OR13(pre, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \
+ (__VAR_OR6(pre, s1, s2, s3, s4, s5, s6) | __VAR_OR7(pre, s7, s8, s9, s10, s11, s12, s13))
+
+#define __VAR_NARGS(...) __VAR_NARGS_(__VA_ARGS__, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
+#define __VAR_NARGS_(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, N, ...) N
+
+#define __VAR_EXPAND(macro, prefix, ...) __VAR_EXPAND_(macro, __VAR_NARGS(__VA_ARGS__), prefix, __VA_ARGS__)
+#define __VAR_EXPAND_(macro, cnt, prefix, ...) __VAR_EXPAND__(macro, cnt, prefix, __VA_ARGS__)
+#define __VAR_EXPAND__(macro, cnt, prefix, ...) __VAR_EXPAND___(macro##cnt, prefix, __VA_ARGS__)
+#define __VAR_EXPAND___(macro, prefix, ...) macro(prefix, __VA_ARGS__)
+
+#define JIO_8_RO(op, name, ...) JIO_8_RO_##op(name, __VA_ARGS__)
+#define JIO_8_RO_RD(name, ...) (*(const volatile uint8_t *)(JA_##name))
+#define JIO_8_RO_WR(name, val) _Static_assert(0, #name " is read-only")
+#define JIO_8_RO_RMW(name, vand, vor) _Static_assert(0, #name " is read-only")
+#define JIO_8_RO_VAR(name, ...) (*(const volatile uint8_t *)(JA_##name))
+
+#define JIO_16_RO(op, name, ...) JIO_16_RO_##op(name, __VA_ARGS__)
+#define JIO_16_RO_RD(name, ...) (*(const volatile uint16_t *)(JA_##name))
+#define JIO_16_RO_WR(name, val) _Static_assert(0, #name " is read-only")
+#define JIO_16_RO_RMW(name, vand, vor) _Static_assert(0, #name " is read-only")
+#define JIO_16_RO_VAR(name, ...) (*(const volatile uint16_t *)(JA_##name))
+
+#define JIO_32_RO(op, name, ...) JIO_32_RO_##op(name, __VA_ARGS__)
+#define JIO_32_RO_RD(name, ...) (*(const volatile uint32_t *)(JA_##name))
+#define JIO_32_RO_WR(name, val) _Static_assert(0, #name " is read-only")
+#define JIO_32_RO_RMW(name, vand, vor) _Static_assert(0, #name " is read-only")
+#define JIO_32_RO_VAR(name, ...) (*(const volatile uint32_t *)(JA_##name))
+
+#define JIO_8_RW(op, name, ...) JIO_8_RW_##op(name, __VA_ARGS__)
+#define JIO_8_RW_RD(name, ...) (*(volatile uint8_t *)(JA_##name))
+#define JIO_8_RW_WR(name, val) (*(volatile uint8_t *)(JA_##name)) = (val)
+#define JIO_8_RW_RMW(name, vand, vor) JIO_8_RW_WR(name, (JIO_8_RW_RD(name) & (vand)) | (vor))
+#define JIO_8_RW_VAR(name, ...) (*(volatile uint8_t *)(JA_##name))
+
+#define JIO_16_RW(op, name, ...) JIO_16_RW_##op(name, __VA_ARGS__)
+#define JIO_16_RW_RD(name, ...) (*(volatile uint16_t *)(JA_##name))
+#define JIO_16_RW_WR(name, val) (*(volatile uint16_t *)(JA_##name)) = (val)
+#define JIO_16_RW_RMW(name, vand, vor) JIO_16_RW_WR(name, (JIO_16_RW_RD(name) & (vand)) | (vor))
+#define JIO_16_RW_VAR(name, ...) (*(volatile uint16_t *)(JA_##name))
+
+#define JIO_32_RW(op, name, ...) JIO_32_RW_##op(name, __VA_ARGS__)
+#define JIO_32_RW_RD(name, ...) (*(volatile uint32_t *)(JA_##name))
+#define JIO_32_RW_WR(name, val) (*(volatile uint32_t *)(JA_##name)) = (val)
+#define JIO_32_RW_RMW(name, vand, vor) JIO_32_RW_WR(name, (JIO_32_RW_RD(name) & (vand)) | (vor))
+#define JIO_32_RW_VAR(name, ...) (*(volatile uint32_t *)(JA_##name))
+
+#define JIO_8_WO(op, name, ...) JIO_8_WO_##op(name, __VA_ARGS__)
+#define JIO_8_WO_RD(name, ...) ({_Static_assert(0, #name " is write-only"); 0;})
+#define JIO_8_WO_WR(name, val) (*(volatile uint8_t *)(JA_##name)) = (val)
+#define JIO_8_WO_RMW(name, vand, vor) JIO_8_WO_WR(name, vor)
+#define JIO_8_WO_VAR(name, ...) (*(volatile uint8_t *)(JA_##name))
+
+#define JIO_16_WO(op, name, ...) JIO_16_WO_##op(name, __VA_ARGS__)
+#define JIO_16_WO_RD(name, ...) ({_Static_assert(0, #name " is write-only"); 0;})
+#define JIO_16_WO_WR(name, val) (*(volatile uint16_t *)(JA_##name)) = (val)
+#define JIO_16_WO_RMW(name, vand, vor) JIO_16_WO_WR(name, vor)
+#define JIO_16_WO_VAR(name, ...) (*(volatile uint16_t *)(JA_##name))
+
+#define JIO_32_WO(op, name, ...) JIO_32_WO_##op(name, __VA_ARGS__)
+#define JIO_32_WO_RD(name, ...) ({_Static_assert(0, #name " is write-only"); 0;})
+#define JIO_32_WO_WR(name, val) (*(volatile uint32_t *)(JA_##name)) = (val)
+#define JIO_32_WO_RMW(name, vand, vor) JIO_32_WO_WR(name, vor)
+#define JIO_32_WO_VAR(name, ...) (*(volatile uint32_t *)(JA_##name))
+
+
+/** __jz_variant
+ *
+ * usage: __jz_variant(register, variant_prefix, variant_postfix)
+ *
+ * effect: expands to register variant given as argument
+ * note: internal usage
+ * note: register must be fully qualified if indexed
+ *
+ * example: __jz_variant(ICOLL_CTRL, , _SET)
+ * example: __jz_variant(ICOLL_ENABLE(3), , _CLR)
+ */
+#define __jz_variant(name, varp, vars) __jz_variant_(JN_##name, JI_##name, varp, vars)
+#define __jz_variant_(...) __jz_variant__(__VA_ARGS__)
+#define __jz_variant__(name, index, varp, vars) varp##name##vars index
+
+/** jz_orf
+ *
+ * usage: jz_orf(register, f1(v1), f2(v2), ...)
+ *
+ * effect: expands to the register value where each field fi has value vi.
+ * Informally: reg_f1(v1) | reg_f2(v2) | ...
+ * note: enumerated values for fields can be obtained by using the syntax:
+ * f1_V(name)
+ *
+ * example: jz_orf(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ */
+#define jz_orf(reg, ...) __VAR_EXPAND(__VAR_OR, BF_##reg##_, __VA_ARGS__)
+
+/** __jz_orfm
+ *
+ * usage: __jz_orfm(register, f1(v1), f2(v2), ...)
+ *
+ * effect: expands to the register value where each field fi has maximum value (vi is ignored).
+ * note: internal usage
+ *
+ * example: __jz_orfm(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ */
+#define __jz_orfm(reg, ...) __VAR_EXPAND(__VAR_OR, BFM_##reg##_, __VA_ARGS__)
+
+/** jz_orm
+ *
+ * usage: jz_orm(register, f1, f2, ...)
+ *
+ * effect: expands to the register value where each field fi is set to its maximum value.
+ * Informally: reg_f1_mask | reg_f2_mask | ...
+ *
+ * example: jz_orm(ICOLL_CTRL, SFTRST, CLKGATE)
+ */
+#define jz_orm(reg, ...) __VAR_EXPAND(__VAR_OR, BM_##reg##_, __VA_ARGS__)
+
+
+/** jz_read
+ *
+ * usage: jz_read(register)
+ *
+ * effect: read a register and return its value
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_read(ICOLL_STATUS)
+ * jz_read(ICOLL_ENABLE(42))
+ */
+#define jz_read(name) JT_##name(RD, name)
+
+/** jz_vreadf
+ *
+ * usage: jz_vreadf(value, register, field)
+ *
+ * effect: given a register value, return the value of a particular field
+ * note: this macro does NOT read any register
+ *
+ * example: jz_vreadf(0xc0000000, ICOLL_CTRL, SFTRST)
+ * jz_vreadf(0x46ff, ICOLL_ENABLE, CPU0_PRIO)
+ */
+#define jz_vreadf(val, name, field) (((val) & BM_##name##_##field) >> BP_##name##_##field)
+
+/** jz_readf
+ *
+ * usage: jz_readf(register, field)
+ *
+ * effect: read a register and return the value of a particular field
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_readf(ICOLL_CTRL, SFTRST)
+ * jz_readf(ICOLL_ENABLE(3), CPU0_PRIO)
+ */
+#define jz_readf(name, field) jz_readf_(jz_read(name), JN_##name, field)
+#define jz_readf_(...) jz_vreadf(__VA_ARGS__)
+
+/** jz_write
+ *
+ * usage: jz_write(register, value)
+ *
+ * effect: write a register
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_write(ICOLL_CTRL, 0x42)
+ * jz_write(ICOLL_ENABLE_SET(3), 0x37)
+ */
+#define jz_write(name, val) JT_##name(WR, name, val)
+
+/** jz_writef
+ *
+ * usage: jz_writef(register, f1(v1), f2(v2), ...)
+ *
+ * effect: change the register value so that field fi has value vi
+ * note: register must be fully qualified if indexed
+ * note: this macro may perform a read-modify-write
+ *
+ * example: jz_writef(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ * jz_writef(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))
+ */
+#define jz_writef(name, ...) jz_writef_(name, JN_##name, __VA_ARGS__)
+#define jz_writef_(name, name2, ...) JT_##name(RMW, name, ~__jz_orfm(name2, __VA_ARGS__), jz_orf(name2, __VA_ARGS__))
+
+/** jz_overwritef
+ *
+ * usage: jz_overwritef(register, f1(v1), f2(v2), ...)
+ *
+ * effect: change the register value so that field fi has value vi and other fields have value zero
+ * thus this macro is equivalent to:
+ * jz_write(register, jz_orf(register, f1(v1), ...))
+ * note: register must be fully qualified if indexed
+ * note: this macro will overwrite the register (it is NOT a read-modify-write)
+ *
+ * example: jz_overwritef(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ * jz_overwritef(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))
+ */
+#define jz_overwritef(name, ...) jz_overwritef_(name, JN_##name, __VA_ARGS__)
+#define jz_overwritef_(name, name2, ...) JT_##name(WR, name, jz_orf(name2, __VA_ARGS__))
+
+/** jz_vwritef
+ *
+ * usage: jz_vwritef(var, register, f1(v1), f2(v2), ...)
+ *
+ * effect: change the variable value so that field fi has value vi
+ * note: this macro will perform a read-modify-write
+ *
+ * example: jz_vwritef(var, ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ * jz_vwritef(var, ICOLL_ENABLE, CPU0_PRIO(1), CPU0_TYPE_V(FIQ))
+ */
+#define jz_vwritef(var, name, ...) (var) = jz_orf(name, __VA_ARGS__) | (~__jz_orfm(name, __VA_ARGS__) & (var))
+
+/** jz_setf
+ *
+ * usage: jz_setf(register, f1, f2, ...)
+ *
+ * effect: change the register value so that field fi has maximum value
+ * IMPORTANT: this macro performs a write to the set variant of the register
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_setf(ICOLL_CTRL, SFTRST, CLKGATE)
+ * jz_setf(ICOLL_ENABLE(3), CPU0_PRIO, CPU0_TYPE)
+ */
+#define jz_setf(name, ...) jz_setf_(__jz_variant(name, , _SET), JN_##name, __VA_ARGS__)
+#define jz_setf_(name, name2, ...) jz_write(name, jz_orm(name2, __VA_ARGS__))
+
+/** jz_clrf
+ *
+ * usage: jz_clrf(register, f1, f2, ...)
+ *
+ * effect: change the register value so that field fi has value zero
+ * IMPORTANT: this macro performs a write to the clr variant of the register
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_clrf(ICOLL_CTRL, SFTRST, CLKGATE)
+ * jz_clrf(ICOLL_ENABLE(3), CPU0_PRIO, CPU0_TYPE)
+ */
+#define jz_clrf(name, ...) jz_clrf_(__jz_variant(name, , _CLR), JN_##name, __VA_ARGS__)
+#define jz_clrf_(name, name2, ...) jz_write(name, jz_orm(name2, __VA_ARGS__))
+
+/** jz_set
+ *
+ * usage: jz_set(register, set_value)
+ *
+ * effect: set some bits using set variant
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_set(ICOLL_CTRL, 0x42)
+ * jz_set(ICOLL_ENABLE(3), 0x37)
+ */
+#define jz_set(name, sval) jz_set_(__jz_variant(name, , _SET), sval)
+#define jz_set_(sname, sval) jz_write(sname, sval)
+
+/** jz_clr
+ *
+ * usage: jz_clr(register, clr_value)
+ *
+ * effect: clear some bits using clr variant
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_clr(ICOLL_CTRL, 0x42)
+ * jz_clr(ICOLL_ENABLE(3), 0x37)
+ */
+#define jz_clr(name, cval) jz_clr_(__jz_variant(name, , _CLR), cval)
+#define jz_clr_(cname, cval) jz_write(cname, cval)
+
+/** jz_cs
+ *
+ * usage: jz_cs(register, clear_value, set_value)
+ *
+ * effect: clear some bits using clr variant and then set some using set variant
+ * note: register must be fully qualified if indexed
+ *
+ * example: jz_cs(ICOLL_CTRL, 0xff, 0x42)
+ * jz_cs(ICOLL_ENABLE(3), 0xff, 0x37)
+ */
+#define jz_cs(name, cval, sval) jz_cs_(__jz_variant(name, , _CLR), __jz_variant(name, , _SET), cval, sval)
+#define jz_cs_(cname, sname, cval, sval) do { jz_write(cname, cval); jz_write(sname, sval); } while(0)
+
+/** jz_csf
+ *
+ * usage: jz_csf(register, f1(v1), f2(v2), ...)
+ *
+ * effect: change the register value so that field fi has value vi using clr and set variants
+ * note: register must be fully qualified if indexed
+ * note: this macro will NOT perform a read-modify-write and is thus safer
+ * IMPORTANT: this macro will set some fields to 0 temporarily, make sure this is acceptable
+ *
+ * example: jz_csf(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))
+ * jz_csf(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))
+ */
+#define jz_csf(name, ...) jz_csf_(name, JN_##name, __VA_ARGS__)
+#define jz_csf_(name, name2, ...) jz_cs(name, __jz_orfm(name2, __VA_ARGS__), jz_orf(name2, __VA_ARGS__))
+
+/** jz_reg
+ *
+ * usage: jz_reg(register)
+ *
+ * effect: return a variable-like expression that can be read/written
+ * note: register must be fully qualified if indexed
+ * note: read-only registers will yield a constant expression
+ *
+ * example: unsigned x = jz_reg(ICOLL_STATUS)
+ * unsigned x = jz_reg(ICOLL_ENABLE(42))
+ * jz_reg(ICOLL_ENABLE(42)) = 64
+ */
+#define jz_reg(name) JT_##name(VAR, name)
+
+
+#endif /* __HEADERGEN_MACRO_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/msc.h b/firmware/target/mips/ingenic_x1000/x1000/msc.h
new file mode 100644
index 0000000000..762b4b1461
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/msc.h
@@ -0,0 +1,824 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_MSC_H__
+#define __HEADERGEN_MSC_H__
+
+#include "macro.h"
+
+#define REG_MSC_CTRL(_n1) jz_reg(MSC_CTRL(_n1))
+#define JA_MSC_CTRL(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x0)
+#define JT_MSC_CTRL(_n1) JIO_32_RW
+#define JN_MSC_CTRL(_n1) MSC_CTRL
+#define JI_MSC_CTRL(_n1) (_n1)
+#define BP_MSC_CTRL_CLOCK 0
+#define BM_MSC_CTRL_CLOCK 0x3
+#define BV_MSC_CTRL_CLOCK__DO_NOTHING 0x0
+#define BV_MSC_CTRL_CLOCK__STOP 0x1
+#define BV_MSC_CTRL_CLOCK__START 0x2
+#define BF_MSC_CTRL_CLOCK(v) (((v) & 0x3) << 0)
+#define BFM_MSC_CTRL_CLOCK(v) BM_MSC_CTRL_CLOCK
+#define BF_MSC_CTRL_CLOCK_V(e) BF_MSC_CTRL_CLOCK(BV_MSC_CTRL_CLOCK__##e)
+#define BFM_MSC_CTRL_CLOCK_V(v) BM_MSC_CTRL_CLOCK
+#define BP_MSC_CTRL_SEND_CCSD 15
+#define BM_MSC_CTRL_SEND_CCSD 0x8000
+#define BF_MSC_CTRL_SEND_CCSD(v) (((v) & 0x1) << 15)
+#define BFM_MSC_CTRL_SEND_CCSD(v) BM_MSC_CTRL_SEND_CCSD
+#define BF_MSC_CTRL_SEND_CCSD_V(e) BF_MSC_CTRL_SEND_CCSD(BV_MSC_CTRL_SEND_CCSD__##e)
+#define BFM_MSC_CTRL_SEND_CCSD_V(v) BM_MSC_CTRL_SEND_CCSD
+#define BP_MSC_CTRL_SEND_AS_CCSD 14
+#define BM_MSC_CTRL_SEND_AS_CCSD 0x4000
+#define BF_MSC_CTRL_SEND_AS_CCSD(v) (((v) & 0x1) << 14)
+#define BFM_MSC_CTRL_SEND_AS_CCSD(v) BM_MSC_CTRL_SEND_AS_CCSD
+#define BF_MSC_CTRL_SEND_AS_CCSD_V(e) BF_MSC_CTRL_SEND_AS_CCSD(BV_MSC_CTRL_SEND_AS_CCSD__##e)
+#define BFM_MSC_CTRL_SEND_AS_CCSD_V(v) BM_MSC_CTRL_SEND_AS_CCSD
+#define BP_MSC_CTRL_EXIT_MULTIPLE 7
+#define BM_MSC_CTRL_EXIT_MULTIPLE 0x80
+#define BF_MSC_CTRL_EXIT_MULTIPLE(v) (((v) & 0x1) << 7)
+#define BFM_MSC_CTRL_EXIT_MULTIPLE(v) BM_MSC_CTRL_EXIT_MULTIPLE
+#define BF_MSC_CTRL_EXIT_MULTIPLE_V(e) BF_MSC_CTRL_EXIT_MULTIPLE(BV_MSC_CTRL_EXIT_MULTIPLE__##e)
+#define BFM_MSC_CTRL_EXIT_MULTIPLE_V(v) BM_MSC_CTRL_EXIT_MULTIPLE
+#define BP_MSC_CTRL_EXIT_TRANSFER 6
+#define BM_MSC_CTRL_EXIT_TRANSFER 0x40
+#define BF_MSC_CTRL_EXIT_TRANSFER(v) (((v) & 0x1) << 6)
+#define BFM_MSC_CTRL_EXIT_TRANSFER(v) BM_MSC_CTRL_EXIT_TRANSFER
+#define BF_MSC_CTRL_EXIT_TRANSFER_V(e) BF_MSC_CTRL_EXIT_TRANSFER(BV_MSC_CTRL_EXIT_TRANSFER__##e)
+#define BFM_MSC_CTRL_EXIT_TRANSFER_V(v) BM_MSC_CTRL_EXIT_TRANSFER
+#define BP_MSC_CTRL_START_READ_WAIT 5
+#define BM_MSC_CTRL_START_READ_WAIT 0x20
+#define BF_MSC_CTRL_START_READ_WAIT(v) (((v) & 0x1) << 5)
+#define BFM_MSC_CTRL_START_READ_WAIT(v) BM_MSC_CTRL_START_READ_WAIT
+#define BF_MSC_CTRL_START_READ_WAIT_V(e) BF_MSC_CTRL_START_READ_WAIT(BV_MSC_CTRL_START_READ_WAIT__##e)
+#define BFM_MSC_CTRL_START_READ_WAIT_V(v) BM_MSC_CTRL_START_READ_WAIT
+#define BP_MSC_CTRL_STOP_READ_WAIT 4
+#define BM_MSC_CTRL_STOP_READ_WAIT 0x10
+#define BF_MSC_CTRL_STOP_READ_WAIT(v) (((v) & 0x1) << 4)
+#define BFM_MSC_CTRL_STOP_READ_WAIT(v) BM_MSC_CTRL_STOP_READ_WAIT
+#define BF_MSC_CTRL_STOP_READ_WAIT_V(e) BF_MSC_CTRL_STOP_READ_WAIT(BV_MSC_CTRL_STOP_READ_WAIT__##e)
+#define BFM_MSC_CTRL_STOP_READ_WAIT_V(v) BM_MSC_CTRL_STOP_READ_WAIT
+#define BP_MSC_CTRL_RESET 3
+#define BM_MSC_CTRL_RESET 0x8
+#define BF_MSC_CTRL_RESET(v) (((v) & 0x1) << 3)
+#define BFM_MSC_CTRL_RESET(v) BM_MSC_CTRL_RESET
+#define BF_MSC_CTRL_RESET_V(e) BF_MSC_CTRL_RESET(BV_MSC_CTRL_RESET__##e)
+#define BFM_MSC_CTRL_RESET_V(v) BM_MSC_CTRL_RESET
+#define BP_MSC_CTRL_START_OP 2
+#define BM_MSC_CTRL_START_OP 0x4
+#define BF_MSC_CTRL_START_OP(v) (((v) & 0x1) << 2)
+#define BFM_MSC_CTRL_START_OP(v) BM_MSC_CTRL_START_OP
+#define BF_MSC_CTRL_START_OP_V(e) BF_MSC_CTRL_START_OP(BV_MSC_CTRL_START_OP__##e)
+#define BFM_MSC_CTRL_START_OP_V(v) BM_MSC_CTRL_START_OP
+
+#define REG_MSC_STAT(_n1) jz_reg(MSC_STAT(_n1))
+#define JA_MSC_STAT(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x4)
+#define JT_MSC_STAT(_n1) JIO_32_RW
+#define JN_MSC_STAT(_n1) MSC_STAT
+#define JI_MSC_STAT(_n1) (_n1)
+#define BP_MSC_STAT_PINS 24
+#define BM_MSC_STAT_PINS 0x1f000000
+#define BF_MSC_STAT_PINS(v) (((v) & 0x1f) << 24)
+#define BFM_MSC_STAT_PINS(v) BM_MSC_STAT_PINS
+#define BF_MSC_STAT_PINS_V(e) BF_MSC_STAT_PINS(BV_MSC_STAT_PINS__##e)
+#define BFM_MSC_STAT_PINS_V(v) BM_MSC_STAT_PINS
+#define BP_MSC_STAT_CRC_WRITE_ERROR 2
+#define BM_MSC_STAT_CRC_WRITE_ERROR 0xc
+#define BV_MSC_STAT_CRC_WRITE_ERROR__NONE 0x0
+#define BV_MSC_STAT_CRC_WRITE_ERROR__BADDATA 0x1
+#define BV_MSC_STAT_CRC_WRITE_ERROR__NOCRC 0x2
+#define BF_MSC_STAT_CRC_WRITE_ERROR(v) (((v) & 0x3) << 2)
+#define BFM_MSC_STAT_CRC_WRITE_ERROR(v) BM_MSC_STAT_CRC_WRITE_ERROR
+#define BF_MSC_STAT_CRC_WRITE_ERROR_V(e) BF_MSC_STAT_CRC_WRITE_ERROR(BV_MSC_STAT_CRC_WRITE_ERROR__##e)
+#define BFM_MSC_STAT_CRC_WRITE_ERROR_V(v) BM_MSC_STAT_CRC_WRITE_ERROR
+#define BP_MSC_STAT_AUTO_CMD12_DONE 31
+#define BM_MSC_STAT_AUTO_CMD12_DONE 0x80000000
+#define BF_MSC_STAT_AUTO_CMD12_DONE(v) (((v) & 0x1) << 31)
+#define BFM_MSC_STAT_AUTO_CMD12_DONE(v) BM_MSC_STAT_AUTO_CMD12_DONE
+#define BF_MSC_STAT_AUTO_CMD12_DONE_V(e) BF_MSC_STAT_AUTO_CMD12_DONE(BV_MSC_STAT_AUTO_CMD12_DONE__##e)
+#define BFM_MSC_STAT_AUTO_CMD12_DONE_V(v) BM_MSC_STAT_AUTO_CMD12_DONE
+#define BP_MSC_STAT_BCE 20
+#define BM_MSC_STAT_BCE 0x100000
+#define BF_MSC_STAT_BCE(v) (((v) & 0x1) << 20)
+#define BFM_MSC_STAT_BCE(v) BM_MSC_STAT_BCE
+#define BF_MSC_STAT_BCE_V(e) BF_MSC_STAT_BCE(BV_MSC_STAT_BCE__##e)
+#define BFM_MSC_STAT_BCE_V(v) BM_MSC_STAT_BCE
+#define BP_MSC_STAT_BDE 19
+#define BM_MSC_STAT_BDE 0x80000
+#define BF_MSC_STAT_BDE(v) (((v) & 0x1) << 19)
+#define BFM_MSC_STAT_BDE(v) BM_MSC_STAT_BDE
+#define BF_MSC_STAT_BDE_V(e) BF_MSC_STAT_BDE(BV_MSC_STAT_BDE__##e)
+#define BFM_MSC_STAT_BDE_V(v) BM_MSC_STAT_BDE
+#define BP_MSC_STAT_BAE 18
+#define BM_MSC_STAT_BAE 0x40000
+#define BF_MSC_STAT_BAE(v) (((v) & 0x1) << 18)
+#define BFM_MSC_STAT_BAE(v) BM_MSC_STAT_BAE
+#define BF_MSC_STAT_BAE_V(e) BF_MSC_STAT_BAE(BV_MSC_STAT_BAE__##e)
+#define BFM_MSC_STAT_BAE_V(v) BM_MSC_STAT_BAE
+#define BP_MSC_STAT_BAR 17
+#define BM_MSC_STAT_BAR 0x20000
+#define BF_MSC_STAT_BAR(v) (((v) & 0x1) << 17)
+#define BFM_MSC_STAT_BAR(v) BM_MSC_STAT_BAR
+#define BF_MSC_STAT_BAR_V(e) BF_MSC_STAT_BAR(BV_MSC_STAT_BAR__##e)
+#define BFM_MSC_STAT_BAR_V(v) BM_MSC_STAT_BAR
+#define BP_MSC_STAT_DMAEND 16
+#define BM_MSC_STAT_DMAEND 0x10000
+#define BF_MSC_STAT_DMAEND(v) (((v) & 0x1) << 16)
+#define BFM_MSC_STAT_DMAEND(v) BM_MSC_STAT_DMAEND
+#define BF_MSC_STAT_DMAEND_V(e) BF_MSC_STAT_DMAEND(BV_MSC_STAT_DMAEND__##e)
+#define BFM_MSC_STAT_DMAEND_V(v) BM_MSC_STAT_DMAEND
+#define BP_MSC_STAT_IS_RESETTING 15
+#define BM_MSC_STAT_IS_RESETTING 0x8000
+#define BF_MSC_STAT_IS_RESETTING(v) (((v) & 0x1) << 15)
+#define BFM_MSC_STAT_IS_RESETTING(v) BM_MSC_STAT_IS_RESETTING
+#define BF_MSC_STAT_IS_RESETTING_V(e) BF_MSC_STAT_IS_RESETTING(BV_MSC_STAT_IS_RESETTING__##e)
+#define BFM_MSC_STAT_IS_RESETTING_V(v) BM_MSC_STAT_IS_RESETTING
+#define BP_MSC_STAT_SDIO_INT_ACTIVE 14
+#define BM_MSC_STAT_SDIO_INT_ACTIVE 0x4000
+#define BF_MSC_STAT_SDIO_INT_ACTIVE(v) (((v) & 0x1) << 14)
+#define BFM_MSC_STAT_SDIO_INT_ACTIVE(v) BM_MSC_STAT_SDIO_INT_ACTIVE
+#define BF_MSC_STAT_SDIO_INT_ACTIVE_V(e) BF_MSC_STAT_SDIO_INT_ACTIVE(BV_MSC_STAT_SDIO_INT_ACTIVE__##e)
+#define BFM_MSC_STAT_SDIO_INT_ACTIVE_V(v) BM_MSC_STAT_SDIO_INT_ACTIVE
+#define BP_MSC_STAT_PROG_DONE 13
+#define BM_MSC_STAT_PROG_DONE 0x2000
+#define BF_MSC_STAT_PROG_DONE(v) (((v) & 0x1) << 13)
+#define BFM_MSC_STAT_PROG_DONE(v) BM_MSC_STAT_PROG_DONE
+#define BF_MSC_STAT_PROG_DONE_V(e) BF_MSC_STAT_PROG_DONE(BV_MSC_STAT_PROG_DONE__##e)
+#define BFM_MSC_STAT_PROG_DONE_V(v) BM_MSC_STAT_PROG_DONE
+#define BP_MSC_STAT_DATA_TRAN_DONE 12
+#define BM_MSC_STAT_DATA_TRAN_DONE 0x1000
+#define BF_MSC_STAT_DATA_TRAN_DONE(v) (((v) & 0x1) << 12)
+#define BFM_MSC_STAT_DATA_TRAN_DONE(v) BM_MSC_STAT_DATA_TRAN_DONE
+#define BF_MSC_STAT_DATA_TRAN_DONE_V(e) BF_MSC_STAT_DATA_TRAN_DONE(BV_MSC_STAT_DATA_TRAN_DONE__##e)
+#define BFM_MSC_STAT_DATA_TRAN_DONE_V(v) BM_MSC_STAT_DATA_TRAN_DONE
+#define BP_MSC_STAT_END_CMD_RES 11
+#define BM_MSC_STAT_END_CMD_RES 0x800
+#define BF_MSC_STAT_END_CMD_RES(v) (((v) & 0x1) << 11)
+#define BFM_MSC_STAT_END_CMD_RES(v) BM_MSC_STAT_END_CMD_RES
+#define BF_MSC_STAT_END_CMD_RES_V(e) BF_MSC_STAT_END_CMD_RES(BV_MSC_STAT_END_CMD_RES__##e)
+#define BFM_MSC_STAT_END_CMD_RES_V(v) BM_MSC_STAT_END_CMD_RES
+#define BP_MSC_STAT_DATA_FIFO_AFULL 10
+#define BM_MSC_STAT_DATA_FIFO_AFULL 0x400
+#define BF_MSC_STAT_DATA_FIFO_AFULL(v) (((v) & 0x1) << 10)
+#define BFM_MSC_STAT_DATA_FIFO_AFULL(v) BM_MSC_STAT_DATA_FIFO_AFULL
+#define BF_MSC_STAT_DATA_FIFO_AFULL_V(e) BF_MSC_STAT_DATA_FIFO_AFULL(BV_MSC_STAT_DATA_FIFO_AFULL__##e)
+#define BFM_MSC_STAT_DATA_FIFO_AFULL_V(v) BM_MSC_STAT_DATA_FIFO_AFULL
+#define BP_MSC_STAT_IS_READ_WAIT 9
+#define BM_MSC_STAT_IS_READ_WAIT 0x200
+#define BF_MSC_STAT_IS_READ_WAIT(v) (((v) & 0x1) << 9)
+#define BFM_MSC_STAT_IS_READ_WAIT(v) BM_MSC_STAT_IS_READ_WAIT
+#define BF_MSC_STAT_IS_READ_WAIT_V(e) BF_MSC_STAT_IS_READ_WAIT(BV_MSC_STAT_IS_READ_WAIT__##e)
+#define BFM_MSC_STAT_IS_READ_WAIT_V(v) BM_MSC_STAT_IS_READ_WAIT
+#define BP_MSC_STAT_CLOCK_EN 8
+#define BM_MSC_STAT_CLOCK_EN 0x100
+#define BF_MSC_STAT_CLOCK_EN(v) (((v) & 0x1) << 8)
+#define BFM_MSC_STAT_CLOCK_EN(v) BM_MSC_STAT_CLOCK_EN
+#define BF_MSC_STAT_CLOCK_EN_V(e) BF_MSC_STAT_CLOCK_EN(BV_MSC_STAT_CLOCK_EN__##e)
+#define BFM_MSC_STAT_CLOCK_EN_V(v) BM_MSC_STAT_CLOCK_EN
+#define BP_MSC_STAT_DATA_FIFO_FULL 7
+#define BM_MSC_STAT_DATA_FIFO_FULL 0x80
+#define BF_MSC_STAT_DATA_FIFO_FULL(v) (((v) & 0x1) << 7)
+#define BFM_MSC_STAT_DATA_FIFO_FULL(v) BM_MSC_STAT_DATA_FIFO_FULL
+#define BF_MSC_STAT_DATA_FIFO_FULL_V(e) BF_MSC_STAT_DATA_FIFO_FULL(BV_MSC_STAT_DATA_FIFO_FULL__##e)
+#define BFM_MSC_STAT_DATA_FIFO_FULL_V(v) BM_MSC_STAT_DATA_FIFO_FULL
+#define BP_MSC_STAT_DATA_FIFO_EMPTY 6
+#define BM_MSC_STAT_DATA_FIFO_EMPTY 0x40
+#define BF_MSC_STAT_DATA_FIFO_EMPTY(v) (((v) & 0x1) << 6)
+#define BFM_MSC_STAT_DATA_FIFO_EMPTY(v) BM_MSC_STAT_DATA_FIFO_EMPTY
+#define BF_MSC_STAT_DATA_FIFO_EMPTY_V(e) BF_MSC_STAT_DATA_FIFO_EMPTY(BV_MSC_STAT_DATA_FIFO_EMPTY__##e)
+#define BFM_MSC_STAT_DATA_FIFO_EMPTY_V(v) BM_MSC_STAT_DATA_FIFO_EMPTY
+#define BP_MSC_STAT_CRC_RES_ERROR 5
+#define BM_MSC_STAT_CRC_RES_ERROR 0x20
+#define BF_MSC_STAT_CRC_RES_ERROR(v) (((v) & 0x1) << 5)
+#define BFM_MSC_STAT_CRC_RES_ERROR(v) BM_MSC_STAT_CRC_RES_ERROR
+#define BF_MSC_STAT_CRC_RES_ERROR_V(e) BF_MSC_STAT_CRC_RES_ERROR(BV_MSC_STAT_CRC_RES_ERROR__##e)
+#define BFM_MSC_STAT_CRC_RES_ERROR_V(v) BM_MSC_STAT_CRC_RES_ERROR
+#define BP_MSC_STAT_CRC_READ_ERROR 4
+#define BM_MSC_STAT_CRC_READ_ERROR 0x10
+#define BF_MSC_STAT_CRC_READ_ERROR(v) (((v) & 0x1) << 4)
+#define BFM_MSC_STAT_CRC_READ_ERROR(v) BM_MSC_STAT_CRC_READ_ERROR
+#define BF_MSC_STAT_CRC_READ_ERROR_V(e) BF_MSC_STAT_CRC_READ_ERROR(BV_MSC_STAT_CRC_READ_ERROR__##e)
+#define BFM_MSC_STAT_CRC_READ_ERROR_V(v) BM_MSC_STAT_CRC_READ_ERROR
+#define BP_MSC_STAT_TIME_OUT_RES 1
+#define BM_MSC_STAT_TIME_OUT_RES 0x2
+#define BF_MSC_STAT_TIME_OUT_RES(v) (((v) & 0x1) << 1)
+#define BFM_MSC_STAT_TIME_OUT_RES(v) BM_MSC_STAT_TIME_OUT_RES
+#define BF_MSC_STAT_TIME_OUT_RES_V(e) BF_MSC_STAT_TIME_OUT_RES(BV_MSC_STAT_TIME_OUT_RES__##e)
+#define BFM_MSC_STAT_TIME_OUT_RES_V(v) BM_MSC_STAT_TIME_OUT_RES
+#define BP_MSC_STAT_TIME_OUT_READ 0
+#define BM_MSC_STAT_TIME_OUT_READ 0x1
+#define BF_MSC_STAT_TIME_OUT_READ(v) (((v) & 0x1) << 0)
+#define BFM_MSC_STAT_TIME_OUT_READ(v) BM_MSC_STAT_TIME_OUT_READ
+#define BF_MSC_STAT_TIME_OUT_READ_V(e) BF_MSC_STAT_TIME_OUT_READ(BV_MSC_STAT_TIME_OUT_READ__##e)
+#define BFM_MSC_STAT_TIME_OUT_READ_V(v) BM_MSC_STAT_TIME_OUT_READ
+
+#define REG_MSC_CMDAT(_n1) jz_reg(MSC_CMDAT(_n1))
+#define JA_MSC_CMDAT(_n1) (0xb3450000 + (_n1) * 0x10000 + 0xc)
+#define JT_MSC_CMDAT(_n1) JIO_32_RW
+#define JN_MSC_CMDAT(_n1) MSC_CMDAT
+#define JI_MSC_CMDAT(_n1) (_n1)
+#define BP_MSC_CMDAT_RTRG 14
+#define BM_MSC_CMDAT_RTRG 0xc000
+#define BV_MSC_CMDAT_RTRG__GE16 0x0
+#define BV_MSC_CMDAT_RTRG__GE32 0x1
+#define BV_MSC_CMDAT_RTRG__GE64 0x2
+#define BV_MSC_CMDAT_RTRG__GE96 0x3
+#define BF_MSC_CMDAT_RTRG(v) (((v) & 0x3) << 14)
+#define BFM_MSC_CMDAT_RTRG(v) BM_MSC_CMDAT_RTRG
+#define BF_MSC_CMDAT_RTRG_V(e) BF_MSC_CMDAT_RTRG(BV_MSC_CMDAT_RTRG__##e)
+#define BFM_MSC_CMDAT_RTRG_V(v) BM_MSC_CMDAT_RTRG
+#define BP_MSC_CMDAT_TTRG 12
+#define BM_MSC_CMDAT_TTRG 0x3000
+#define BV_MSC_CMDAT_TTRG__LE16 0x0
+#define BV_MSC_CMDAT_TTRG__LE32 0x1
+#define BV_MSC_CMDAT_TTRG__LE64 0x2
+#define BV_MSC_CMDAT_TTRG__LE96 0x3
+#define BF_MSC_CMDAT_TTRG(v) (((v) & 0x3) << 12)
+#define BFM_MSC_CMDAT_TTRG(v) BM_MSC_CMDAT_TTRG
+#define BF_MSC_CMDAT_TTRG_V(e) BF_MSC_CMDAT_TTRG(BV_MSC_CMDAT_TTRG__##e)
+#define BFM_MSC_CMDAT_TTRG_V(v) BM_MSC_CMDAT_TTRG
+#define BP_MSC_CMDAT_BUS_WIDTH 9
+#define BM_MSC_CMDAT_BUS_WIDTH 0x600
+#define BV_MSC_CMDAT_BUS_WIDTH__1BIT 0x0
+#define BV_MSC_CMDAT_BUS_WIDTH__4BIT 0x2
+#define BV_MSC_CMDAT_BUS_WIDTH__8BIT 0x3
+#define BF_MSC_CMDAT_BUS_WIDTH(v) (((v) & 0x3) << 9)
+#define BFM_MSC_CMDAT_BUS_WIDTH(v) BM_MSC_CMDAT_BUS_WIDTH
+#define BF_MSC_CMDAT_BUS_WIDTH_V(e) BF_MSC_CMDAT_BUS_WIDTH(BV_MSC_CMDAT_BUS_WIDTH__##e)
+#define BFM_MSC_CMDAT_BUS_WIDTH_V(v) BM_MSC_CMDAT_BUS_WIDTH
+#define BP_MSC_CMDAT_RESP_FMT 0
+#define BM_MSC_CMDAT_RESP_FMT 0x7
+#define BF_MSC_CMDAT_RESP_FMT(v) (((v) & 0x7) << 0)
+#define BFM_MSC_CMDAT_RESP_FMT(v) BM_MSC_CMDAT_RESP_FMT
+#define BF_MSC_CMDAT_RESP_FMT_V(e) BF_MSC_CMDAT_RESP_FMT(BV_MSC_CMDAT_RESP_FMT__##e)
+#define BFM_MSC_CMDAT_RESP_FMT_V(v) BM_MSC_CMDAT_RESP_FMT
+#define BP_MSC_CMDAT_CCS_EXPECTED 31
+#define BM_MSC_CMDAT_CCS_EXPECTED 0x80000000
+#define BF_MSC_CMDAT_CCS_EXPECTED(v) (((v) & 0x1) << 31)
+#define BFM_MSC_CMDAT_CCS_EXPECTED(v) BM_MSC_CMDAT_CCS_EXPECTED
+#define BF_MSC_CMDAT_CCS_EXPECTED_V(e) BF_MSC_CMDAT_CCS_EXPECTED(BV_MSC_CMDAT_CCS_EXPECTED__##e)
+#define BFM_MSC_CMDAT_CCS_EXPECTED_V(v) BM_MSC_CMDAT_CCS_EXPECTED
+#define BP_MSC_CMDAT_READ_CEATA 30
+#define BM_MSC_CMDAT_READ_CEATA 0x40000000
+#define BF_MSC_CMDAT_READ_CEATA(v) (((v) & 0x1) << 30)
+#define BFM_MSC_CMDAT_READ_CEATA(v) BM_MSC_CMDAT_READ_CEATA
+#define BF_MSC_CMDAT_READ_CEATA_V(e) BF_MSC_CMDAT_READ_CEATA(BV_MSC_CMDAT_READ_CEATA__##e)
+#define BFM_MSC_CMDAT_READ_CEATA_V(v) BM_MSC_CMDAT_READ_CEATA
+#define BP_MSC_CMDAT_DIS_BOOT 27
+#define BM_MSC_CMDAT_DIS_BOOT 0x8000000
+#define BF_MSC_CMDAT_DIS_BOOT(v) (((v) & 0x1) << 27)
+#define BFM_MSC_CMDAT_DIS_BOOT(v) BM_MSC_CMDAT_DIS_BOOT
+#define BF_MSC_CMDAT_DIS_BOOT_V(e) BF_MSC_CMDAT_DIS_BOOT(BV_MSC_CMDAT_DIS_BOOT__##e)
+#define BFM_MSC_CMDAT_DIS_BOOT_V(v) BM_MSC_CMDAT_DIS_BOOT
+#define BP_MSC_CMDAT_EXP_BOOT_ACK 25
+#define BM_MSC_CMDAT_EXP_BOOT_ACK 0x2000000
+#define BF_MSC_CMDAT_EXP_BOOT_ACK(v) (((v) & 0x1) << 25)
+#define BFM_MSC_CMDAT_EXP_BOOT_ACK(v) BM_MSC_CMDAT_EXP_BOOT_ACK
+#define BF_MSC_CMDAT_EXP_BOOT_ACK_V(e) BF_MSC_CMDAT_EXP_BOOT_ACK(BV_MSC_CMDAT_EXP_BOOT_ACK__##e)
+#define BFM_MSC_CMDAT_EXP_BOOT_ACK_V(v) BM_MSC_CMDAT_EXP_BOOT_ACK
+#define BP_MSC_CMDAT_BOOT_MODE 24
+#define BM_MSC_CMDAT_BOOT_MODE 0x1000000
+#define BF_MSC_CMDAT_BOOT_MODE(v) (((v) & 0x1) << 24)
+#define BFM_MSC_CMDAT_BOOT_MODE(v) BM_MSC_CMDAT_BOOT_MODE
+#define BF_MSC_CMDAT_BOOT_MODE_V(e) BF_MSC_CMDAT_BOOT_MODE(BV_MSC_CMDAT_BOOT_MODE__##e)
+#define BFM_MSC_CMDAT_BOOT_MODE_V(v) BM_MSC_CMDAT_BOOT_MODE
+#define BP_MSC_CMDAT_SDIO_PRDT 17
+#define BM_MSC_CMDAT_SDIO_PRDT 0x20000
+#define BF_MSC_CMDAT_SDIO_PRDT(v) (((v) & 0x1) << 17)
+#define BFM_MSC_CMDAT_SDIO_PRDT(v) BM_MSC_CMDAT_SDIO_PRDT
+#define BF_MSC_CMDAT_SDIO_PRDT_V(e) BF_MSC_CMDAT_SDIO_PRDT(BV_MSC_CMDAT_SDIO_PRDT__##e)
+#define BFM_MSC_CMDAT_SDIO_PRDT_V(v) BM_MSC_CMDAT_SDIO_PRDT
+#define BP_MSC_CMDAT_AUTO_CMD12 16
+#define BM_MSC_CMDAT_AUTO_CMD12 0x10000
+#define BF_MSC_CMDAT_AUTO_CMD12(v) (((v) & 0x1) << 16)
+#define BFM_MSC_CMDAT_AUTO_CMD12(v) BM_MSC_CMDAT_AUTO_CMD12
+#define BF_MSC_CMDAT_AUTO_CMD12_V(e) BF_MSC_CMDAT_AUTO_CMD12(BV_MSC_CMDAT_AUTO_CMD12__##e)
+#define BFM_MSC_CMDAT_AUTO_CMD12_V(v) BM_MSC_CMDAT_AUTO_CMD12
+#define BP_MSC_CMDAT_IO_ABORT 11
+#define BM_MSC_CMDAT_IO_ABORT 0x800
+#define BF_MSC_CMDAT_IO_ABORT(v) (((v) & 0x1) << 11)
+#define BFM_MSC_CMDAT_IO_ABORT(v) BM_MSC_CMDAT_IO_ABORT
+#define BF_MSC_CMDAT_IO_ABORT_V(e) BF_MSC_CMDAT_IO_ABORT(BV_MSC_CMDAT_IO_ABORT__##e)
+#define BFM_MSC_CMDAT_IO_ABORT_V(v) BM_MSC_CMDAT_IO_ABORT
+#define BP_MSC_CMDAT_INIT 7
+#define BM_MSC_CMDAT_INIT 0x80
+#define BF_MSC_CMDAT_INIT(v) (((v) & 0x1) << 7)
+#define BFM_MSC_CMDAT_INIT(v) BM_MSC_CMDAT_INIT
+#define BF_MSC_CMDAT_INIT_V(e) BF_MSC_CMDAT_INIT(BV_MSC_CMDAT_INIT__##e)
+#define BFM_MSC_CMDAT_INIT_V(v) BM_MSC_CMDAT_INIT
+#define BP_MSC_CMDAT_BUSY 6
+#define BM_MSC_CMDAT_BUSY 0x40
+#define BF_MSC_CMDAT_BUSY(v) (((v) & 0x1) << 6)
+#define BFM_MSC_CMDAT_BUSY(v) BM_MSC_CMDAT_BUSY
+#define BF_MSC_CMDAT_BUSY_V(e) BF_MSC_CMDAT_BUSY(BV_MSC_CMDAT_BUSY__##e)
+#define BFM_MSC_CMDAT_BUSY_V(v) BM_MSC_CMDAT_BUSY
+#define BP_MSC_CMDAT_STREAM_BLOCK 5
+#define BM_MSC_CMDAT_STREAM_BLOCK 0x20
+#define BF_MSC_CMDAT_STREAM_BLOCK(v) (((v) & 0x1) << 5)
+#define BFM_MSC_CMDAT_STREAM_BLOCK(v) BM_MSC_CMDAT_STREAM_BLOCK
+#define BF_MSC_CMDAT_STREAM_BLOCK_V(e) BF_MSC_CMDAT_STREAM_BLOCK(BV_MSC_CMDAT_STREAM_BLOCK__##e)
+#define BFM_MSC_CMDAT_STREAM_BLOCK_V(v) BM_MSC_CMDAT_STREAM_BLOCK
+#define BP_MSC_CMDAT_WRITE_READ 4
+#define BM_MSC_CMDAT_WRITE_READ 0x10
+#define BF_MSC_CMDAT_WRITE_READ(v) (((v) & 0x1) << 4)
+#define BFM_MSC_CMDAT_WRITE_READ(v) BM_MSC_CMDAT_WRITE_READ
+#define BF_MSC_CMDAT_WRITE_READ_V(e) BF_MSC_CMDAT_WRITE_READ(BV_MSC_CMDAT_WRITE_READ__##e)
+#define BFM_MSC_CMDAT_WRITE_READ_V(v) BM_MSC_CMDAT_WRITE_READ
+#define BP_MSC_CMDAT_DATA_EN 3
+#define BM_MSC_CMDAT_DATA_EN 0x8
+#define BF_MSC_CMDAT_DATA_EN(v) (((v) & 0x1) << 3)
+#define BFM_MSC_CMDAT_DATA_EN(v) BM_MSC_CMDAT_DATA_EN
+#define BF_MSC_CMDAT_DATA_EN_V(e) BF_MSC_CMDAT_DATA_EN(BV_MSC_CMDAT_DATA_EN__##e)
+#define BFM_MSC_CMDAT_DATA_EN_V(v) BM_MSC_CMDAT_DATA_EN
+
+#define REG_MSC_IMASK(_n1) jz_reg(MSC_IMASK(_n1))
+#define JA_MSC_IMASK(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x24)
+#define JT_MSC_IMASK(_n1) JIO_32_RW
+#define JN_MSC_IMASK(_n1) MSC_IMASK
+#define JI_MSC_IMASK(_n1) (_n1)
+#define BP_MSC_IMASK_PINS 24
+#define BM_MSC_IMASK_PINS 0x1f000000
+#define BF_MSC_IMASK_PINS(v) (((v) & 0x1f) << 24)
+#define BFM_MSC_IMASK_PINS(v) BM_MSC_IMASK_PINS
+#define BF_MSC_IMASK_PINS_V(e) BF_MSC_IMASK_PINS(BV_MSC_IMASK_PINS__##e)
+#define BFM_MSC_IMASK_PINS_V(v) BM_MSC_IMASK_PINS
+#define BP_MSC_IMASK_DMA_DATA_DONE 31
+#define BM_MSC_IMASK_DMA_DATA_DONE 0x80000000
+#define BF_MSC_IMASK_DMA_DATA_DONE(v) (((v) & 0x1) << 31)
+#define BFM_MSC_IMASK_DMA_DATA_DONE(v) BM_MSC_IMASK_DMA_DATA_DONE
+#define BF_MSC_IMASK_DMA_DATA_DONE_V(e) BF_MSC_IMASK_DMA_DATA_DONE(BV_MSC_IMASK_DMA_DATA_DONE__##e)
+#define BFM_MSC_IMASK_DMA_DATA_DONE_V(v) BM_MSC_IMASK_DMA_DATA_DONE
+#define BP_MSC_IMASK_WR_ALL_DONE 23
+#define BM_MSC_IMASK_WR_ALL_DONE 0x800000
+#define BF_MSC_IMASK_WR_ALL_DONE(v) (((v) & 0x1) << 23)
+#define BFM_MSC_IMASK_WR_ALL_DONE(v) BM_MSC_IMASK_WR_ALL_DONE
+#define BF_MSC_IMASK_WR_ALL_DONE_V(e) BF_MSC_IMASK_WR_ALL_DONE(BV_MSC_IMASK_WR_ALL_DONE__##e)
+#define BFM_MSC_IMASK_WR_ALL_DONE_V(v) BM_MSC_IMASK_WR_ALL_DONE
+#define BP_MSC_IMASK_BCE 20
+#define BM_MSC_IMASK_BCE 0x100000
+#define BF_MSC_IMASK_BCE(v) (((v) & 0x1) << 20)
+#define BFM_MSC_IMASK_BCE(v) BM_MSC_IMASK_BCE
+#define BF_MSC_IMASK_BCE_V(e) BF_MSC_IMASK_BCE(BV_MSC_IMASK_BCE__##e)
+#define BFM_MSC_IMASK_BCE_V(v) BM_MSC_IMASK_BCE
+#define BP_MSC_IMASK_BDE 19
+#define BM_MSC_IMASK_BDE 0x80000
+#define BF_MSC_IMASK_BDE(v) (((v) & 0x1) << 19)
+#define BFM_MSC_IMASK_BDE(v) BM_MSC_IMASK_BDE
+#define BF_MSC_IMASK_BDE_V(e) BF_MSC_IMASK_BDE(BV_MSC_IMASK_BDE__##e)
+#define BFM_MSC_IMASK_BDE_V(v) BM_MSC_IMASK_BDE
+#define BP_MSC_IMASK_BAE 18
+#define BM_MSC_IMASK_BAE 0x40000
+#define BF_MSC_IMASK_BAE(v) (((v) & 0x1) << 18)
+#define BFM_MSC_IMASK_BAE(v) BM_MSC_IMASK_BAE
+#define BF_MSC_IMASK_BAE_V(e) BF_MSC_IMASK_BAE(BV_MSC_IMASK_BAE__##e)
+#define BFM_MSC_IMASK_BAE_V(v) BM_MSC_IMASK_BAE
+#define BP_MSC_IMASK_BAR 17
+#define BM_MSC_IMASK_BAR 0x20000
+#define BF_MSC_IMASK_BAR(v) (((v) & 0x1) << 17)
+#define BFM_MSC_IMASK_BAR(v) BM_MSC_IMASK_BAR
+#define BF_MSC_IMASK_BAR_V(e) BF_MSC_IMASK_BAR(BV_MSC_IMASK_BAR__##e)
+#define BFM_MSC_IMASK_BAR_V(v) BM_MSC_IMASK_BAR
+#define BP_MSC_IMASK_DMAEND 16
+#define BM_MSC_IMASK_DMAEND 0x10000
+#define BF_MSC_IMASK_DMAEND(v) (((v) & 0x1) << 16)
+#define BFM_MSC_IMASK_DMAEND(v) BM_MSC_IMASK_DMAEND
+#define BF_MSC_IMASK_DMAEND_V(e) BF_MSC_IMASK_DMAEND(BV_MSC_IMASK_DMAEND__##e)
+#define BFM_MSC_IMASK_DMAEND_V(v) BM_MSC_IMASK_DMAEND
+#define BP_MSC_IMASK_AUTO_CMD12_DONE 15
+#define BM_MSC_IMASK_AUTO_CMD12_DONE 0x8000
+#define BF_MSC_IMASK_AUTO_CMD12_DONE(v) (((v) & 0x1) << 15)
+#define BFM_MSC_IMASK_AUTO_CMD12_DONE(v) BM_MSC_IMASK_AUTO_CMD12_DONE
+#define BF_MSC_IMASK_AUTO_CMD12_DONE_V(e) BF_MSC_IMASK_AUTO_CMD12_DONE(BV_MSC_IMASK_AUTO_CMD12_DONE__##e)
+#define BFM_MSC_IMASK_AUTO_CMD12_DONE_V(v) BM_MSC_IMASK_AUTO_CMD12_DONE
+#define BP_MSC_IMASK_DATA_FIFO_FULL 14
+#define BM_MSC_IMASK_DATA_FIFO_FULL 0x4000
+#define BF_MSC_IMASK_DATA_FIFO_FULL(v) (((v) & 0x1) << 14)
+#define BFM_MSC_IMASK_DATA_FIFO_FULL(v) BM_MSC_IMASK_DATA_FIFO_FULL
+#define BF_MSC_IMASK_DATA_FIFO_FULL_V(e) BF_MSC_IMASK_DATA_FIFO_FULL(BV_MSC_IMASK_DATA_FIFO_FULL__##e)
+#define BFM_MSC_IMASK_DATA_FIFO_FULL_V(v) BM_MSC_IMASK_DATA_FIFO_FULL
+#define BP_MSC_IMASK_DATA_FIFO_EMPTY 13
+#define BM_MSC_IMASK_DATA_FIFO_EMPTY 0x2000
+#define BF_MSC_IMASK_DATA_FIFO_EMPTY(v) (((v) & 0x1) << 13)
+#define BFM_MSC_IMASK_DATA_FIFO_EMPTY(v) BM_MSC_IMASK_DATA_FIFO_EMPTY
+#define BF_MSC_IMASK_DATA_FIFO_EMPTY_V(e) BF_MSC_IMASK_DATA_FIFO_EMPTY(BV_MSC_IMASK_DATA_FIFO_EMPTY__##e)
+#define BFM_MSC_IMASK_DATA_FIFO_EMPTY_V(v) BM_MSC_IMASK_DATA_FIFO_EMPTY
+#define BP_MSC_IMASK_CRC_RES_ERROR 12
+#define BM_MSC_IMASK_CRC_RES_ERROR 0x1000
+#define BF_MSC_IMASK_CRC_RES_ERROR(v) (((v) & 0x1) << 12)
+#define BFM_MSC_IMASK_CRC_RES_ERROR(v) BM_MSC_IMASK_CRC_RES_ERROR
+#define BF_MSC_IMASK_CRC_RES_ERROR_V(e) BF_MSC_IMASK_CRC_RES_ERROR(BV_MSC_IMASK_CRC_RES_ERROR__##e)
+#define BFM_MSC_IMASK_CRC_RES_ERROR_V(v) BM_MSC_IMASK_CRC_RES_ERROR
+#define BP_MSC_IMASK_CRC_READ_ERROR 11
+#define BM_MSC_IMASK_CRC_READ_ERROR 0x800
+#define BF_MSC_IMASK_CRC_READ_ERROR(v) (((v) & 0x1) << 11)
+#define BFM_MSC_IMASK_CRC_READ_ERROR(v) BM_MSC_IMASK_CRC_READ_ERROR
+#define BF_MSC_IMASK_CRC_READ_ERROR_V(e) BF_MSC_IMASK_CRC_READ_ERROR(BV_MSC_IMASK_CRC_READ_ERROR__##e)
+#define BFM_MSC_IMASK_CRC_READ_ERROR_V(v) BM_MSC_IMASK_CRC_READ_ERROR
+#define BP_MSC_IMASK_CRC_WRITE_ERROR 10
+#define BM_MSC_IMASK_CRC_WRITE_ERROR 0x400
+#define BF_MSC_IMASK_CRC_WRITE_ERROR(v) (((v) & 0x1) << 10)
+#define BFM_MSC_IMASK_CRC_WRITE_ERROR(v) BM_MSC_IMASK_CRC_WRITE_ERROR
+#define BF_MSC_IMASK_CRC_WRITE_ERROR_V(e) BF_MSC_IMASK_CRC_WRITE_ERROR(BV_MSC_IMASK_CRC_WRITE_ERROR__##e)
+#define BFM_MSC_IMASK_CRC_WRITE_ERROR_V(v) BM_MSC_IMASK_CRC_WRITE_ERROR
+#define BP_MSC_IMASK_TIME_OUT_RES 9
+#define BM_MSC_IMASK_TIME_OUT_RES 0x200
+#define BF_MSC_IMASK_TIME_OUT_RES(v) (((v) & 0x1) << 9)
+#define BFM_MSC_IMASK_TIME_OUT_RES(v) BM_MSC_IMASK_TIME_OUT_RES
+#define BF_MSC_IMASK_TIME_OUT_RES_V(e) BF_MSC_IMASK_TIME_OUT_RES(BV_MSC_IMASK_TIME_OUT_RES__##e)
+#define BFM_MSC_IMASK_TIME_OUT_RES_V(v) BM_MSC_IMASK_TIME_OUT_RES
+#define BP_MSC_IMASK_TIME_OUT_READ 8
+#define BM_MSC_IMASK_TIME_OUT_READ 0x100
+#define BF_MSC_IMASK_TIME_OUT_READ(v) (((v) & 0x1) << 8)
+#define BFM_MSC_IMASK_TIME_OUT_READ(v) BM_MSC_IMASK_TIME_OUT_READ
+#define BF_MSC_IMASK_TIME_OUT_READ_V(e) BF_MSC_IMASK_TIME_OUT_READ(BV_MSC_IMASK_TIME_OUT_READ__##e)
+#define BFM_MSC_IMASK_TIME_OUT_READ_V(v) BM_MSC_IMASK_TIME_OUT_READ
+#define BP_MSC_IMASK_SDIO 7
+#define BM_MSC_IMASK_SDIO 0x80
+#define BF_MSC_IMASK_SDIO(v) (((v) & 0x1) << 7)
+#define BFM_MSC_IMASK_SDIO(v) BM_MSC_IMASK_SDIO
+#define BF_MSC_IMASK_SDIO_V(e) BF_MSC_IMASK_SDIO(BV_MSC_IMASK_SDIO__##e)
+#define BFM_MSC_IMASK_SDIO_V(v) BM_MSC_IMASK_SDIO
+#define BP_MSC_IMASK_TXFIFO_WR_REQ 6
+#define BM_MSC_IMASK_TXFIFO_WR_REQ 0x40
+#define BF_MSC_IMASK_TXFIFO_WR_REQ(v) (((v) & 0x1) << 6)
+#define BFM_MSC_IMASK_TXFIFO_WR_REQ(v) BM_MSC_IMASK_TXFIFO_WR_REQ
+#define BF_MSC_IMASK_TXFIFO_WR_REQ_V(e) BF_MSC_IMASK_TXFIFO_WR_REQ(BV_MSC_IMASK_TXFIFO_WR_REQ__##e)
+#define BFM_MSC_IMASK_TXFIFO_WR_REQ_V(v) BM_MSC_IMASK_TXFIFO_WR_REQ
+#define BP_MSC_IMASK_RXFIFO_RD_REQ 5
+#define BM_MSC_IMASK_RXFIFO_RD_REQ 0x20
+#define BF_MSC_IMASK_RXFIFO_RD_REQ(v) (((v) & 0x1) << 5)
+#define BFM_MSC_IMASK_RXFIFO_RD_REQ(v) BM_MSC_IMASK_RXFIFO_RD_REQ
+#define BF_MSC_IMASK_RXFIFO_RD_REQ_V(e) BF_MSC_IMASK_RXFIFO_RD_REQ(BV_MSC_IMASK_RXFIFO_RD_REQ__##e)
+#define BFM_MSC_IMASK_RXFIFO_RD_REQ_V(v) BM_MSC_IMASK_RXFIFO_RD_REQ
+#define BP_MSC_IMASK_END_CMD_RES 2
+#define BM_MSC_IMASK_END_CMD_RES 0x4
+#define BF_MSC_IMASK_END_CMD_RES(v) (((v) & 0x1) << 2)
+#define BFM_MSC_IMASK_END_CMD_RES(v) BM_MSC_IMASK_END_CMD_RES
+#define BF_MSC_IMASK_END_CMD_RES_V(e) BF_MSC_IMASK_END_CMD_RES(BV_MSC_IMASK_END_CMD_RES__##e)
+#define BFM_MSC_IMASK_END_CMD_RES_V(v) BM_MSC_IMASK_END_CMD_RES
+#define BP_MSC_IMASK_PROG_DONE 1
+#define BM_MSC_IMASK_PROG_DONE 0x2
+#define BF_MSC_IMASK_PROG_DONE(v) (((v) & 0x1) << 1)
+#define BFM_MSC_IMASK_PROG_DONE(v) BM_MSC_IMASK_PROG_DONE
+#define BF_MSC_IMASK_PROG_DONE_V(e) BF_MSC_IMASK_PROG_DONE(BV_MSC_IMASK_PROG_DONE__##e)
+#define BFM_MSC_IMASK_PROG_DONE_V(v) BM_MSC_IMASK_PROG_DONE
+#define BP_MSC_IMASK_DATA_TRAN_DONE 0
+#define BM_MSC_IMASK_DATA_TRAN_DONE 0x1
+#define BF_MSC_IMASK_DATA_TRAN_DONE(v) (((v) & 0x1) << 0)
+#define BFM_MSC_IMASK_DATA_TRAN_DONE(v) BM_MSC_IMASK_DATA_TRAN_DONE
+#define BF_MSC_IMASK_DATA_TRAN_DONE_V(e) BF_MSC_IMASK_DATA_TRAN_DONE(BV_MSC_IMASK_DATA_TRAN_DONE__##e)
+#define BFM_MSC_IMASK_DATA_TRAN_DONE_V(v) BM_MSC_IMASK_DATA_TRAN_DONE
+
+#define REG_MSC_IFLAG(_n1) jz_reg(MSC_IFLAG(_n1))
+#define JA_MSC_IFLAG(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x28)
+#define JT_MSC_IFLAG(_n1) JIO_32_RW
+#define JN_MSC_IFLAG(_n1) MSC_IFLAG
+#define JI_MSC_IFLAG(_n1) (_n1)
+#define BP_MSC_IFLAG_PINS 24
+#define BM_MSC_IFLAG_PINS 0x1f000000
+#define BF_MSC_IFLAG_PINS(v) (((v) & 0x1f) << 24)
+#define BFM_MSC_IFLAG_PINS(v) BM_MSC_IFLAG_PINS
+#define BF_MSC_IFLAG_PINS_V(e) BF_MSC_IFLAG_PINS(BV_MSC_IFLAG_PINS__##e)
+#define BFM_MSC_IFLAG_PINS_V(v) BM_MSC_IFLAG_PINS
+#define BP_MSC_IFLAG_DMA_DATA_DONE 31
+#define BM_MSC_IFLAG_DMA_DATA_DONE 0x80000000
+#define BF_MSC_IFLAG_DMA_DATA_DONE(v) (((v) & 0x1) << 31)
+#define BFM_MSC_IFLAG_DMA_DATA_DONE(v) BM_MSC_IFLAG_DMA_DATA_DONE
+#define BF_MSC_IFLAG_DMA_DATA_DONE_V(e) BF_MSC_IFLAG_DMA_DATA_DONE(BV_MSC_IFLAG_DMA_DATA_DONE__##e)
+#define BFM_MSC_IFLAG_DMA_DATA_DONE_V(v) BM_MSC_IFLAG_DMA_DATA_DONE
+#define BP_MSC_IFLAG_WR_ALL_DONE 23
+#define BM_MSC_IFLAG_WR_ALL_DONE 0x800000
+#define BF_MSC_IFLAG_WR_ALL_DONE(v) (((v) & 0x1) << 23)
+#define BFM_MSC_IFLAG_WR_ALL_DONE(v) BM_MSC_IFLAG_WR_ALL_DONE
+#define BF_MSC_IFLAG_WR_ALL_DONE_V(e) BF_MSC_IFLAG_WR_ALL_DONE(BV_MSC_IFLAG_WR_ALL_DONE__##e)
+#define BFM_MSC_IFLAG_WR_ALL_DONE_V(v) BM_MSC_IFLAG_WR_ALL_DONE
+#define BP_MSC_IFLAG_BCE 20
+#define BM_MSC_IFLAG_BCE 0x100000
+#define BF_MSC_IFLAG_BCE(v) (((v) & 0x1) << 20)
+#define BFM_MSC_IFLAG_BCE(v) BM_MSC_IFLAG_BCE
+#define BF_MSC_IFLAG_BCE_V(e) BF_MSC_IFLAG_BCE(BV_MSC_IFLAG_BCE__##e)
+#define BFM_MSC_IFLAG_BCE_V(v) BM_MSC_IFLAG_BCE
+#define BP_MSC_IFLAG_BDE 19
+#define BM_MSC_IFLAG_BDE 0x80000
+#define BF_MSC_IFLAG_BDE(v) (((v) & 0x1) << 19)
+#define BFM_MSC_IFLAG_BDE(v) BM_MSC_IFLAG_BDE
+#define BF_MSC_IFLAG_BDE_V(e) BF_MSC_IFLAG_BDE(BV_MSC_IFLAG_BDE__##e)
+#define BFM_MSC_IFLAG_BDE_V(v) BM_MSC_IFLAG_BDE
+#define BP_MSC_IFLAG_BAE 18
+#define BM_MSC_IFLAG_BAE 0x40000
+#define BF_MSC_IFLAG_BAE(v) (((v) & 0x1) << 18)
+#define BFM_MSC_IFLAG_BAE(v) BM_MSC_IFLAG_BAE
+#define BF_MSC_IFLAG_BAE_V(e) BF_MSC_IFLAG_BAE(BV_MSC_IFLAG_BAE__##e)
+#define BFM_MSC_IFLAG_BAE_V(v) BM_MSC_IFLAG_BAE
+#define BP_MSC_IFLAG_BAR 17
+#define BM_MSC_IFLAG_BAR 0x20000
+#define BF_MSC_IFLAG_BAR(v) (((v) & 0x1) << 17)
+#define BFM_MSC_IFLAG_BAR(v) BM_MSC_IFLAG_BAR
+#define BF_MSC_IFLAG_BAR_V(e) BF_MSC_IFLAG_BAR(BV_MSC_IFLAG_BAR__##e)
+#define BFM_MSC_IFLAG_BAR_V(v) BM_MSC_IFLAG_BAR
+#define BP_MSC_IFLAG_DMAEND 16
+#define BM_MSC_IFLAG_DMAEND 0x10000
+#define BF_MSC_IFLAG_DMAEND(v) (((v) & 0x1) << 16)
+#define BFM_MSC_IFLAG_DMAEND(v) BM_MSC_IFLAG_DMAEND
+#define BF_MSC_IFLAG_DMAEND_V(e) BF_MSC_IFLAG_DMAEND(BV_MSC_IFLAG_DMAEND__##e)
+#define BFM_MSC_IFLAG_DMAEND_V(v) BM_MSC_IFLAG_DMAEND
+#define BP_MSC_IFLAG_AUTO_CMD12_DONE 15
+#define BM_MSC_IFLAG_AUTO_CMD12_DONE 0x8000
+#define BF_MSC_IFLAG_AUTO_CMD12_DONE(v) (((v) & 0x1) << 15)
+#define BFM_MSC_IFLAG_AUTO_CMD12_DONE(v) BM_MSC_IFLAG_AUTO_CMD12_DONE
+#define BF_MSC_IFLAG_AUTO_CMD12_DONE_V(e) BF_MSC_IFLAG_AUTO_CMD12_DONE(BV_MSC_IFLAG_AUTO_CMD12_DONE__##e)
+#define BFM_MSC_IFLAG_AUTO_CMD12_DONE_V(v) BM_MSC_IFLAG_AUTO_CMD12_DONE
+#define BP_MSC_IFLAG_DATA_FIFO_FULL 14
+#define BM_MSC_IFLAG_DATA_FIFO_FULL 0x4000
+#define BF_MSC_IFLAG_DATA_FIFO_FULL(v) (((v) & 0x1) << 14)
+#define BFM_MSC_IFLAG_DATA_FIFO_FULL(v) BM_MSC_IFLAG_DATA_FIFO_FULL
+#define BF_MSC_IFLAG_DATA_FIFO_FULL_V(e) BF_MSC_IFLAG_DATA_FIFO_FULL(BV_MSC_IFLAG_DATA_FIFO_FULL__##e)
+#define BFM_MSC_IFLAG_DATA_FIFO_FULL_V(v) BM_MSC_IFLAG_DATA_FIFO_FULL
+#define BP_MSC_IFLAG_DATA_FIFO_EMPTY 13
+#define BM_MSC_IFLAG_DATA_FIFO_EMPTY 0x2000
+#define BF_MSC_IFLAG_DATA_FIFO_EMPTY(v) (((v) & 0x1) << 13)
+#define BFM_MSC_IFLAG_DATA_FIFO_EMPTY(v) BM_MSC_IFLAG_DATA_FIFO_EMPTY
+#define BF_MSC_IFLAG_DATA_FIFO_EMPTY_V(e) BF_MSC_IFLAG_DATA_FIFO_EMPTY(BV_MSC_IFLAG_DATA_FIFO_EMPTY__##e)
+#define BFM_MSC_IFLAG_DATA_FIFO_EMPTY_V(v) BM_MSC_IFLAG_DATA_FIFO_EMPTY
+#define BP_MSC_IFLAG_CRC_RES_ERROR 12
+#define BM_MSC_IFLAG_CRC_RES_ERROR 0x1000
+#define BF_MSC_IFLAG_CRC_RES_ERROR(v) (((v) & 0x1) << 12)
+#define BFM_MSC_IFLAG_CRC_RES_ERROR(v) BM_MSC_IFLAG_CRC_RES_ERROR
+#define BF_MSC_IFLAG_CRC_RES_ERROR_V(e) BF_MSC_IFLAG_CRC_RES_ERROR(BV_MSC_IFLAG_CRC_RES_ERROR__##e)
+#define BFM_MSC_IFLAG_CRC_RES_ERROR_V(v) BM_MSC_IFLAG_CRC_RES_ERROR
+#define BP_MSC_IFLAG_CRC_READ_ERROR 11
+#define BM_MSC_IFLAG_CRC_READ_ERROR 0x800
+#define BF_MSC_IFLAG_CRC_READ_ERROR(v) (((v) & 0x1) << 11)
+#define BFM_MSC_IFLAG_CRC_READ_ERROR(v) BM_MSC_IFLAG_CRC_READ_ERROR
+#define BF_MSC_IFLAG_CRC_READ_ERROR_V(e) BF_MSC_IFLAG_CRC_READ_ERROR(BV_MSC_IFLAG_CRC_READ_ERROR__##e)
+#define BFM_MSC_IFLAG_CRC_READ_ERROR_V(v) BM_MSC_IFLAG_CRC_READ_ERROR
+#define BP_MSC_IFLAG_CRC_WRITE_ERROR 10
+#define BM_MSC_IFLAG_CRC_WRITE_ERROR 0x400
+#define BF_MSC_IFLAG_CRC_WRITE_ERROR(v) (((v) & 0x1) << 10)
+#define BFM_MSC_IFLAG_CRC_WRITE_ERROR(v) BM_MSC_IFLAG_CRC_WRITE_ERROR
+#define BF_MSC_IFLAG_CRC_WRITE_ERROR_V(e) BF_MSC_IFLAG_CRC_WRITE_ERROR(BV_MSC_IFLAG_CRC_WRITE_ERROR__##e)
+#define BFM_MSC_IFLAG_CRC_WRITE_ERROR_V(v) BM_MSC_IFLAG_CRC_WRITE_ERROR
+#define BP_MSC_IFLAG_TIME_OUT_RES 9
+#define BM_MSC_IFLAG_TIME_OUT_RES 0x200
+#define BF_MSC_IFLAG_TIME_OUT_RES(v) (((v) & 0x1) << 9)
+#define BFM_MSC_IFLAG_TIME_OUT_RES(v) BM_MSC_IFLAG_TIME_OUT_RES
+#define BF_MSC_IFLAG_TIME_OUT_RES_V(e) BF_MSC_IFLAG_TIME_OUT_RES(BV_MSC_IFLAG_TIME_OUT_RES__##e)
+#define BFM_MSC_IFLAG_TIME_OUT_RES_V(v) BM_MSC_IFLAG_TIME_OUT_RES
+#define BP_MSC_IFLAG_TIME_OUT_READ 8
+#define BM_MSC_IFLAG_TIME_OUT_READ 0x100
+#define BF_MSC_IFLAG_TIME_OUT_READ(v) (((v) & 0x1) << 8)
+#define BFM_MSC_IFLAG_TIME_OUT_READ(v) BM_MSC_IFLAG_TIME_OUT_READ
+#define BF_MSC_IFLAG_TIME_OUT_READ_V(e) BF_MSC_IFLAG_TIME_OUT_READ(BV_MSC_IFLAG_TIME_OUT_READ__##e)
+#define BFM_MSC_IFLAG_TIME_OUT_READ_V(v) BM_MSC_IFLAG_TIME_OUT_READ
+#define BP_MSC_IFLAG_SDIO 7
+#define BM_MSC_IFLAG_SDIO 0x80
+#define BF_MSC_IFLAG_SDIO(v) (((v) & 0x1) << 7)
+#define BFM_MSC_IFLAG_SDIO(v) BM_MSC_IFLAG_SDIO
+#define BF_MSC_IFLAG_SDIO_V(e) BF_MSC_IFLAG_SDIO(BV_MSC_IFLAG_SDIO__##e)
+#define BFM_MSC_IFLAG_SDIO_V(v) BM_MSC_IFLAG_SDIO
+#define BP_MSC_IFLAG_TXFIFO_WR_REQ 6
+#define BM_MSC_IFLAG_TXFIFO_WR_REQ 0x40
+#define BF_MSC_IFLAG_TXFIFO_WR_REQ(v) (((v) & 0x1) << 6)
+#define BFM_MSC_IFLAG_TXFIFO_WR_REQ(v) BM_MSC_IFLAG_TXFIFO_WR_REQ
+#define BF_MSC_IFLAG_TXFIFO_WR_REQ_V(e) BF_MSC_IFLAG_TXFIFO_WR_REQ(BV_MSC_IFLAG_TXFIFO_WR_REQ__##e)
+#define BFM_MSC_IFLAG_TXFIFO_WR_REQ_V(v) BM_MSC_IFLAG_TXFIFO_WR_REQ
+#define BP_MSC_IFLAG_RXFIFO_RD_REQ 5
+#define BM_MSC_IFLAG_RXFIFO_RD_REQ 0x20
+#define BF_MSC_IFLAG_RXFIFO_RD_REQ(v) (((v) & 0x1) << 5)
+#define BFM_MSC_IFLAG_RXFIFO_RD_REQ(v) BM_MSC_IFLAG_RXFIFO_RD_REQ
+#define BF_MSC_IFLAG_RXFIFO_RD_REQ_V(e) BF_MSC_IFLAG_RXFIFO_RD_REQ(BV_MSC_IFLAG_RXFIFO_RD_REQ__##e)
+#define BFM_MSC_IFLAG_RXFIFO_RD_REQ_V(v) BM_MSC_IFLAG_RXFIFO_RD_REQ
+#define BP_MSC_IFLAG_END_CMD_RES 2
+#define BM_MSC_IFLAG_END_CMD_RES 0x4
+#define BF_MSC_IFLAG_END_CMD_RES(v) (((v) & 0x1) << 2)
+#define BFM_MSC_IFLAG_END_CMD_RES(v) BM_MSC_IFLAG_END_CMD_RES
+#define BF_MSC_IFLAG_END_CMD_RES_V(e) BF_MSC_IFLAG_END_CMD_RES(BV_MSC_IFLAG_END_CMD_RES__##e)
+#define BFM_MSC_IFLAG_END_CMD_RES_V(v) BM_MSC_IFLAG_END_CMD_RES
+#define BP_MSC_IFLAG_PROG_DONE 1
+#define BM_MSC_IFLAG_PROG_DONE 0x2
+#define BF_MSC_IFLAG_PROG_DONE(v) (((v) & 0x1) << 1)
+#define BFM_MSC_IFLAG_PROG_DONE(v) BM_MSC_IFLAG_PROG_DONE
+#define BF_MSC_IFLAG_PROG_DONE_V(e) BF_MSC_IFLAG_PROG_DONE(BV_MSC_IFLAG_PROG_DONE__##e)
+#define BFM_MSC_IFLAG_PROG_DONE_V(v) BM_MSC_IFLAG_PROG_DONE
+#define BP_MSC_IFLAG_DATA_TRAN_DONE 0
+#define BM_MSC_IFLAG_DATA_TRAN_DONE 0x1
+#define BF_MSC_IFLAG_DATA_TRAN_DONE(v) (((v) & 0x1) << 0)
+#define BFM_MSC_IFLAG_DATA_TRAN_DONE(v) BM_MSC_IFLAG_DATA_TRAN_DONE
+#define BF_MSC_IFLAG_DATA_TRAN_DONE_V(e) BF_MSC_IFLAG_DATA_TRAN_DONE(BV_MSC_IFLAG_DATA_TRAN_DONE__##e)
+#define BFM_MSC_IFLAG_DATA_TRAN_DONE_V(v) BM_MSC_IFLAG_DATA_TRAN_DONE
+
+#define REG_MSC_LPM(_n1) jz_reg(MSC_LPM(_n1))
+#define JA_MSC_LPM(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x40)
+#define JT_MSC_LPM(_n1) JIO_32_RW
+#define JN_MSC_LPM(_n1) MSC_LPM
+#define JI_MSC_LPM(_n1) (_n1)
+#define BP_MSC_LPM_DRV_SEL 30
+#define BM_MSC_LPM_DRV_SEL 0xc0000000
+#define BV_MSC_LPM_DRV_SEL__FALL_EDGE 0x0
+#define BV_MSC_LPM_DRV_SEL__RISE_EDGE_DELAY_1NS 0x1
+#define BV_MSC_LPM_DRV_SEL__RISE_EDGE_DELAY_QTR_PHASE 0x2
+#define BF_MSC_LPM_DRV_SEL(v) (((v) & 0x3) << 30)
+#define BFM_MSC_LPM_DRV_SEL(v) BM_MSC_LPM_DRV_SEL
+#define BF_MSC_LPM_DRV_SEL_V(e) BF_MSC_LPM_DRV_SEL(BV_MSC_LPM_DRV_SEL__##e)
+#define BFM_MSC_LPM_DRV_SEL_V(v) BM_MSC_LPM_DRV_SEL
+#define BP_MSC_LPM_SMP_SEL 28
+#define BM_MSC_LPM_SMP_SEL 0x30000000
+#define BV_MSC_LPM_SMP_SEL__RISE_EDGE 0x0
+#define BV_MSC_LPM_SMP_SEL__RISE_EDGE_DELAYED 0x1
+#define BF_MSC_LPM_SMP_SEL(v) (((v) & 0x3) << 28)
+#define BFM_MSC_LPM_SMP_SEL(v) BM_MSC_LPM_SMP_SEL
+#define BF_MSC_LPM_SMP_SEL_V(e) BF_MSC_LPM_SMP_SEL(BV_MSC_LPM_SMP_SEL__##e)
+#define BFM_MSC_LPM_SMP_SEL_V(v) BM_MSC_LPM_SMP_SEL
+#define BP_MSC_LPM_ENABLE 0
+#define BM_MSC_LPM_ENABLE 0x1
+#define BF_MSC_LPM_ENABLE(v) (((v) & 0x1) << 0)
+#define BFM_MSC_LPM_ENABLE(v) BM_MSC_LPM_ENABLE
+#define BF_MSC_LPM_ENABLE_V(e) BF_MSC_LPM_ENABLE(BV_MSC_LPM_ENABLE__##e)
+#define BFM_MSC_LPM_ENABLE_V(v) BM_MSC_LPM_ENABLE
+
+#define REG_MSC_DMAC(_n1) jz_reg(MSC_DMAC(_n1))
+#define JA_MSC_DMAC(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x44)
+#define JT_MSC_DMAC(_n1) JIO_32_RW
+#define JN_MSC_DMAC(_n1) MSC_DMAC
+#define JI_MSC_DMAC(_n1) (_n1)
+#define BP_MSC_DMAC_ADDR_OFFSET 5
+#define BM_MSC_DMAC_ADDR_OFFSET 0x60
+#define BF_MSC_DMAC_ADDR_OFFSET(v) (((v) & 0x3) << 5)
+#define BFM_MSC_DMAC_ADDR_OFFSET(v) BM_MSC_DMAC_ADDR_OFFSET
+#define BF_MSC_DMAC_ADDR_OFFSET_V(e) BF_MSC_DMAC_ADDR_OFFSET(BV_MSC_DMAC_ADDR_OFFSET__##e)
+#define BFM_MSC_DMAC_ADDR_OFFSET_V(v) BM_MSC_DMAC_ADDR_OFFSET
+#define BP_MSC_DMAC_INCR 2
+#define BM_MSC_DMAC_INCR 0xc
+#define BF_MSC_DMAC_INCR(v) (((v) & 0x3) << 2)
+#define BFM_MSC_DMAC_INCR(v) BM_MSC_DMAC_INCR
+#define BF_MSC_DMAC_INCR_V(e) BF_MSC_DMAC_INCR(BV_MSC_DMAC_INCR__##e)
+#define BFM_MSC_DMAC_INCR_V(v) BM_MSC_DMAC_INCR
+#define BP_MSC_DMAC_MODE_SEL 7
+#define BM_MSC_DMAC_MODE_SEL 0x80
+#define BF_MSC_DMAC_MODE_SEL(v) (((v) & 0x1) << 7)
+#define BFM_MSC_DMAC_MODE_SEL(v) BM_MSC_DMAC_MODE_SEL
+#define BF_MSC_DMAC_MODE_SEL_V(e) BF_MSC_DMAC_MODE_SEL(BV_MSC_DMAC_MODE_SEL__##e)
+#define BFM_MSC_DMAC_MODE_SEL_V(v) BM_MSC_DMAC_MODE_SEL
+#define BP_MSC_DMAC_ALIGN_EN 4
+#define BM_MSC_DMAC_ALIGN_EN 0x10
+#define BF_MSC_DMAC_ALIGN_EN(v) (((v) & 0x1) << 4)
+#define BFM_MSC_DMAC_ALIGN_EN(v) BM_MSC_DMAC_ALIGN_EN
+#define BF_MSC_DMAC_ALIGN_EN_V(e) BF_MSC_DMAC_ALIGN_EN(BV_MSC_DMAC_ALIGN_EN__##e)
+#define BFM_MSC_DMAC_ALIGN_EN_V(v) BM_MSC_DMAC_ALIGN_EN
+#define BP_MSC_DMAC_DMASEL 1
+#define BM_MSC_DMAC_DMASEL 0x2
+#define BF_MSC_DMAC_DMASEL(v) (((v) & 0x1) << 1)
+#define BFM_MSC_DMAC_DMASEL(v) BM_MSC_DMAC_DMASEL
+#define BF_MSC_DMAC_DMASEL_V(e) BF_MSC_DMAC_DMASEL(BV_MSC_DMAC_DMASEL__##e)
+#define BFM_MSC_DMAC_DMASEL_V(v) BM_MSC_DMAC_DMASEL
+#define BP_MSC_DMAC_ENABLE 0
+#define BM_MSC_DMAC_ENABLE 0x1
+#define BF_MSC_DMAC_ENABLE(v) (((v) & 0x1) << 0)
+#define BFM_MSC_DMAC_ENABLE(v) BM_MSC_DMAC_ENABLE
+#define BF_MSC_DMAC_ENABLE_V(e) BF_MSC_DMAC_ENABLE(BV_MSC_DMAC_ENABLE__##e)
+#define BFM_MSC_DMAC_ENABLE_V(v) BM_MSC_DMAC_ENABLE
+
+#define REG_MSC_CTRL2(_n1) jz_reg(MSC_CTRL2(_n1))
+#define JA_MSC_CTRL2(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x58)
+#define JT_MSC_CTRL2(_n1) JIO_32_RW
+#define JN_MSC_CTRL2(_n1) MSC_CTRL2
+#define JI_MSC_CTRL2(_n1) (_n1)
+#define BP_MSC_CTRL2_PIN_INT_POLARITY 24
+#define BM_MSC_CTRL2_PIN_INT_POLARITY 0x1f000000
+#define BF_MSC_CTRL2_PIN_INT_POLARITY(v) (((v) & 0x1f) << 24)
+#define BFM_MSC_CTRL2_PIN_INT_POLARITY(v) BM_MSC_CTRL2_PIN_INT_POLARITY
+#define BF_MSC_CTRL2_PIN_INT_POLARITY_V(e) BF_MSC_CTRL2_PIN_INT_POLARITY(BV_MSC_CTRL2_PIN_INT_POLARITY__##e)
+#define BFM_MSC_CTRL2_PIN_INT_POLARITY_V(v) BM_MSC_CTRL2_PIN_INT_POLARITY
+#define BP_MSC_CTRL2_SPEED 0
+#define BM_MSC_CTRL2_SPEED 0x7
+#define BV_MSC_CTRL2_SPEED__DEFAULT 0x0
+#define BV_MSC_CTRL2_SPEED__HIGHSPEED 0x1
+#define BV_MSC_CTRL2_SPEED__SDR12 0x2
+#define BV_MSC_CTRL2_SPEED__SDR25 0x3
+#define BV_MSC_CTRL2_SPEED__SDR50 0x4
+#define BF_MSC_CTRL2_SPEED(v) (((v) & 0x7) << 0)
+#define BFM_MSC_CTRL2_SPEED(v) BM_MSC_CTRL2_SPEED
+#define BF_MSC_CTRL2_SPEED_V(e) BF_MSC_CTRL2_SPEED(BV_MSC_CTRL2_SPEED__##e)
+#define BFM_MSC_CTRL2_SPEED_V(v) BM_MSC_CTRL2_SPEED
+#define BP_MSC_CTRL2_STPRM 4
+#define BM_MSC_CTRL2_STPRM 0x10
+#define BF_MSC_CTRL2_STPRM(v) (((v) & 0x1) << 4)
+#define BFM_MSC_CTRL2_STPRM(v) BM_MSC_CTRL2_STPRM
+#define BF_MSC_CTRL2_STPRM_V(e) BF_MSC_CTRL2_STPRM(BV_MSC_CTRL2_STPRM__##e)
+#define BFM_MSC_CTRL2_STPRM_V(v) BM_MSC_CTRL2_STPRM
+
+#define REG_MSC_CLKRT(_n1) jz_reg(MSC_CLKRT(_n1))
+#define JA_MSC_CLKRT(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x8)
+#define JT_MSC_CLKRT(_n1) JIO_32_RW
+#define JN_MSC_CLKRT(_n1) MSC_CLKRT
+#define JI_MSC_CLKRT(_n1) (_n1)
+
+#define REG_MSC_RESTO(_n1) jz_reg(MSC_RESTO(_n1))
+#define JA_MSC_RESTO(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x10)
+#define JT_MSC_RESTO(_n1) JIO_32_RW
+#define JN_MSC_RESTO(_n1) MSC_RESTO
+#define JI_MSC_RESTO(_n1) (_n1)
+
+#define REG_MSC_RDTO(_n1) jz_reg(MSC_RDTO(_n1))
+#define JA_MSC_RDTO(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x14)
+#define JT_MSC_RDTO(_n1) JIO_32_RW
+#define JN_MSC_RDTO(_n1) MSC_RDTO
+#define JI_MSC_RDTO(_n1) (_n1)
+
+#define REG_MSC_BLKLEN(_n1) jz_reg(MSC_BLKLEN(_n1))
+#define JA_MSC_BLKLEN(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x18)
+#define JT_MSC_BLKLEN(_n1) JIO_32_RW
+#define JN_MSC_BLKLEN(_n1) MSC_BLKLEN
+#define JI_MSC_BLKLEN(_n1) (_n1)
+
+#define REG_MSC_NOB(_n1) jz_reg(MSC_NOB(_n1))
+#define JA_MSC_NOB(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x1c)
+#define JT_MSC_NOB(_n1) JIO_32_RW
+#define JN_MSC_NOB(_n1) MSC_NOB
+#define JI_MSC_NOB(_n1) (_n1)
+
+#define REG_MSC_SNOB(_n1) jz_reg(MSC_SNOB(_n1))
+#define JA_MSC_SNOB(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x20)
+#define JT_MSC_SNOB(_n1) JIO_32_RW
+#define JN_MSC_SNOB(_n1) MSC_SNOB
+#define JI_MSC_SNOB(_n1) (_n1)
+
+#define REG_MSC_CMD(_n1) jz_reg(MSC_CMD(_n1))
+#define JA_MSC_CMD(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x2c)
+#define JT_MSC_CMD(_n1) JIO_32_RW
+#define JN_MSC_CMD(_n1) MSC_CMD
+#define JI_MSC_CMD(_n1) (_n1)
+
+#define REG_MSC_ARG(_n1) jz_reg(MSC_ARG(_n1))
+#define JA_MSC_ARG(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x30)
+#define JT_MSC_ARG(_n1) JIO_32_RW
+#define JN_MSC_ARG(_n1) MSC_ARG
+#define JI_MSC_ARG(_n1) (_n1)
+
+#define REG_MSC_RES(_n1) jz_reg(MSC_RES(_n1))
+#define JA_MSC_RES(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x34)
+#define JT_MSC_RES(_n1) JIO_32_RW
+#define JN_MSC_RES(_n1) MSC_RES
+#define JI_MSC_RES(_n1) (_n1)
+
+#define REG_MSC_RXFIFO(_n1) jz_reg(MSC_RXFIFO(_n1))
+#define JA_MSC_RXFIFO(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x38)
+#define JT_MSC_RXFIFO(_n1) JIO_32_RW
+#define JN_MSC_RXFIFO(_n1) MSC_RXFIFO
+#define JI_MSC_RXFIFO(_n1) (_n1)
+
+#define REG_MSC_TXFIFO(_n1) jz_reg(MSC_TXFIFO(_n1))
+#define JA_MSC_TXFIFO(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x3c)
+#define JT_MSC_TXFIFO(_n1) JIO_32_RW
+#define JN_MSC_TXFIFO(_n1) MSC_TXFIFO
+#define JI_MSC_TXFIFO(_n1) (_n1)
+
+#define REG_MSC_DMANDA(_n1) jz_reg(MSC_DMANDA(_n1))
+#define JA_MSC_DMANDA(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x48)
+#define JT_MSC_DMANDA(_n1) JIO_32_RW
+#define JN_MSC_DMANDA(_n1) MSC_DMANDA
+#define JI_MSC_DMANDA(_n1) (_n1)
+
+#define REG_MSC_DMADA(_n1) jz_reg(MSC_DMADA(_n1))
+#define JA_MSC_DMADA(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x4c)
+#define JT_MSC_DMADA(_n1) JIO_32_RW
+#define JN_MSC_DMADA(_n1) MSC_DMADA
+#define JI_MSC_DMADA(_n1) (_n1)
+
+#define REG_MSC_DMALEN(_n1) jz_reg(MSC_DMALEN(_n1))
+#define JA_MSC_DMALEN(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x50)
+#define JT_MSC_DMALEN(_n1) JIO_32_RW
+#define JN_MSC_DMALEN(_n1) MSC_DMALEN
+#define JI_MSC_DMALEN(_n1) (_n1)
+
+#define REG_MSC_DMACMD(_n1) jz_reg(MSC_DMACMD(_n1))
+#define JA_MSC_DMACMD(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x54)
+#define JT_MSC_DMACMD(_n1) JIO_32_RW
+#define JN_MSC_DMACMD(_n1) MSC_DMACMD
+#define JI_MSC_DMACMD(_n1) (_n1)
+
+#define REG_MSC_RTCNT(_n1) jz_reg(MSC_RTCNT(_n1))
+#define JA_MSC_RTCNT(_n1) (0xb3450000 + (_n1) * 0x10000 + 0x5c)
+#define JT_MSC_RTCNT(_n1) JIO_32_RW
+#define JN_MSC_RTCNT(_n1) MSC_RTCNT
+#define JI_MSC_RTCNT(_n1) (_n1)
+
+#endif /* __HEADERGEN_MSC_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ost.h b/firmware/target/mips/ingenic_x1000/x1000/ost.h
new file mode 100644
index 0000000000..8f2619e0e7
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/ost.h
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_OST_H__
+#define __HEADERGEN_OST_H__
+
+#include "macro.h"
+
+#define REG_OST_CTRL jz_reg(OST_CTRL)
+#define JA_OST_CTRL (0xb2000000 + 0x0)
+#define JT_OST_CTRL JIO_32_RW
+#define JN_OST_CTRL OST_CTRL
+#define JI_OST_CTRL
+#define BP_OST_CTRL_PRESCALE2 3
+#define BM_OST_CTRL_PRESCALE2 0x38
+#define BV_OST_CTRL_PRESCALE2__BY_1 0x0
+#define BV_OST_CTRL_PRESCALE2__BY_4 0x1
+#define BV_OST_CTRL_PRESCALE2__BY_16 0x2
+#define BF_OST_CTRL_PRESCALE2(v) (((v) & 0x7) << 3)
+#define BFM_OST_CTRL_PRESCALE2(v) BM_OST_CTRL_PRESCALE2
+#define BF_OST_CTRL_PRESCALE2_V(e) BF_OST_CTRL_PRESCALE2(BV_OST_CTRL_PRESCALE2__##e)
+#define BFM_OST_CTRL_PRESCALE2_V(v) BM_OST_CTRL_PRESCALE2
+#define BP_OST_CTRL_PRESCALE1 0
+#define BM_OST_CTRL_PRESCALE1 0x7
+#define BV_OST_CTRL_PRESCALE1__BY_1 0x0
+#define BV_OST_CTRL_PRESCALE1__BY_4 0x1
+#define BV_OST_CTRL_PRESCALE1__BY_16 0x2
+#define BF_OST_CTRL_PRESCALE1(v) (((v) & 0x7) << 0)
+#define BFM_OST_CTRL_PRESCALE1(v) BM_OST_CTRL_PRESCALE1
+#define BF_OST_CTRL_PRESCALE1_V(e) BF_OST_CTRL_PRESCALE1(BV_OST_CTRL_PRESCALE1__##e)
+#define BFM_OST_CTRL_PRESCALE1_V(v) BM_OST_CTRL_PRESCALE1
+
+#define REG_OST_ENABLE jz_reg(OST_ENABLE)
+#define JA_OST_ENABLE (0xb2000000 + 0x4)
+#define JT_OST_ENABLE JIO_32_RW
+#define JN_OST_ENABLE OST_ENABLE
+#define JI_OST_ENABLE
+#define REG_OST_ENABLE_SET jz_reg(OST_ENABLE_SET)
+#define JA_OST_ENABLE_SET (JA_OST_ENABLE + 0x30)
+#define JT_OST_ENABLE_SET JIO_32_WO
+#define JN_OST_ENABLE_SET OST_ENABLE
+#define JI_OST_ENABLE_SET
+#define REG_OST_ENABLE_CLR jz_reg(OST_ENABLE_CLR)
+#define JA_OST_ENABLE_CLR (JA_OST_ENABLE + 0x34)
+#define JT_OST_ENABLE_CLR JIO_32_WO
+#define JN_OST_ENABLE_CLR OST_ENABLE
+#define JI_OST_ENABLE_CLR
+#define BP_OST_ENABLE_OST1 0
+#define BM_OST_ENABLE_OST1 0x1
+#define BF_OST_ENABLE_OST1(v) (((v) & 0x1) << 0)
+#define BFM_OST_ENABLE_OST1(v) BM_OST_ENABLE_OST1
+#define BF_OST_ENABLE_OST1_V(e) BF_OST_ENABLE_OST1(BV_OST_ENABLE_OST1__##e)
+#define BFM_OST_ENABLE_OST1_V(v) BM_OST_ENABLE_OST1
+#define BP_OST_ENABLE_OST2 1
+#define BM_OST_ENABLE_OST2 0x2
+#define BF_OST_ENABLE_OST2(v) (((v) & 0x1) << 1)
+#define BFM_OST_ENABLE_OST2(v) BM_OST_ENABLE_OST2
+#define BF_OST_ENABLE_OST2_V(e) BF_OST_ENABLE_OST2(BV_OST_ENABLE_OST2__##e)
+#define BFM_OST_ENABLE_OST2_V(v) BM_OST_ENABLE_OST2
+
+#define REG_OST_CLEAR jz_reg(OST_CLEAR)
+#define JA_OST_CLEAR (0xb2000000 + 0x8)
+#define JT_OST_CLEAR JIO_32_RW
+#define JN_OST_CLEAR OST_CLEAR
+#define JI_OST_CLEAR
+#define BP_OST_CLEAR_OST1 0
+#define BM_OST_CLEAR_OST1 0x1
+#define BF_OST_CLEAR_OST1(v) (((v) & 0x1) << 0)
+#define BFM_OST_CLEAR_OST1(v) BM_OST_CLEAR_OST1
+#define BF_OST_CLEAR_OST1_V(e) BF_OST_CLEAR_OST1(BV_OST_CLEAR_OST1__##e)
+#define BFM_OST_CLEAR_OST1_V(v) BM_OST_CLEAR_OST1
+#define BP_OST_CLEAR_OST2 1
+#define BM_OST_CLEAR_OST2 0x2
+#define BF_OST_CLEAR_OST2(v) (((v) & 0x1) << 1)
+#define BFM_OST_CLEAR_OST2(v) BM_OST_CLEAR_OST2
+#define BF_OST_CLEAR_OST2_V(e) BF_OST_CLEAR_OST2(BV_OST_CLEAR_OST2__##e)
+#define BFM_OST_CLEAR_OST2_V(v) BM_OST_CLEAR_OST2
+
+#define REG_OST_1FLG jz_reg(OST_1FLG)
+#define JA_OST_1FLG (0xb2000000 + 0xc)
+#define JT_OST_1FLG JIO_32_RW
+#define JN_OST_1FLG OST_1FLG
+#define JI_OST_1FLG
+
+#define REG_OST_1MSK jz_reg(OST_1MSK)
+#define JA_OST_1MSK (0xb2000000 + 0x10)
+#define JT_OST_1MSK JIO_32_RW
+#define JN_OST_1MSK OST_1MSK
+#define JI_OST_1MSK
+
+#define REG_OST_1DFR jz_reg(OST_1DFR)
+#define JA_OST_1DFR (0xb2000000 + 0x14)
+#define JT_OST_1DFR JIO_32_RW
+#define JN_OST_1DFR OST_1DFR
+#define JI_OST_1DFR
+
+#define REG_OST_1CNT jz_reg(OST_1CNT)
+#define JA_OST_1CNT (0xb2000000 + 0x18)
+#define JT_OST_1CNT JIO_32_RW
+#define JN_OST_1CNT OST_1CNT
+#define JI_OST_1CNT
+
+#define REG_OST_2CNTH jz_reg(OST_2CNTH)
+#define JA_OST_2CNTH (0xb2000000 + 0x1c)
+#define JT_OST_2CNTH JIO_32_RW
+#define JN_OST_2CNTH OST_2CNTH
+#define JI_OST_2CNTH
+
+#define REG_OST_2CNTL jz_reg(OST_2CNTL)
+#define JA_OST_2CNTL (0xb2000000 + 0x20)
+#define JT_OST_2CNTL JIO_32_RW
+#define JN_OST_2CNTL OST_2CNTL
+#define JI_OST_2CNTL
+
+#define REG_OST_2CNTHB jz_reg(OST_2CNTHB)
+#define JA_OST_2CNTHB (0xb2000000 + 0x24)
+#define JT_OST_2CNTHB JIO_32_RW
+#define JN_OST_2CNTHB OST_2CNTHB
+#define JI_OST_2CNTHB
+
+#endif /* __HEADERGEN_OST_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/rtc.h b/firmware/target/mips/ingenic_x1000/x1000/rtc.h
new file mode 100644
index 0000000000..38f82e7d0d
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/rtc.h
@@ -0,0 +1,221 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_RTC_H__
+#define __HEADERGEN_RTC_H__
+
+#include "macro.h"
+
+#define REG_RTC_CR jz_reg(RTC_CR)
+#define JA_RTC_CR (0xb0003000 + 0x0)
+#define JT_RTC_CR JIO_32_RW
+#define JN_RTC_CR RTC_CR
+#define JI_RTC_CR
+#define BP_RTC_CR_WRDY 7
+#define BM_RTC_CR_WRDY 0x80
+#define BF_RTC_CR_WRDY(v) (((v) & 0x1) << 7)
+#define BFM_RTC_CR_WRDY(v) BM_RTC_CR_WRDY
+#define BF_RTC_CR_WRDY_V(e) BF_RTC_CR_WRDY(BV_RTC_CR_WRDY__##e)
+#define BFM_RTC_CR_WRDY_V(v) BM_RTC_CR_WRDY
+#define BP_RTC_CR_1HZ 6
+#define BM_RTC_CR_1HZ 0x40
+#define BF_RTC_CR_1HZ(v) (((v) & 0x1) << 6)
+#define BFM_RTC_CR_1HZ(v) BM_RTC_CR_1HZ
+#define BF_RTC_CR_1HZ_V(e) BF_RTC_CR_1HZ(BV_RTC_CR_1HZ__##e)
+#define BFM_RTC_CR_1HZ_V(v) BM_RTC_CR_1HZ
+#define BP_RTC_CR_1HZIE 5
+#define BM_RTC_CR_1HZIE 0x20
+#define BF_RTC_CR_1HZIE(v) (((v) & 0x1) << 5)
+#define BFM_RTC_CR_1HZIE(v) BM_RTC_CR_1HZIE
+#define BF_RTC_CR_1HZIE_V(e) BF_RTC_CR_1HZIE(BV_RTC_CR_1HZIE__##e)
+#define BFM_RTC_CR_1HZIE_V(v) BM_RTC_CR_1HZIE
+#define BP_RTC_CR_AF 4
+#define BM_RTC_CR_AF 0x10
+#define BF_RTC_CR_AF(v) (((v) & 0x1) << 4)
+#define BFM_RTC_CR_AF(v) BM_RTC_CR_AF
+#define BF_RTC_CR_AF_V(e) BF_RTC_CR_AF(BV_RTC_CR_AF__##e)
+#define BFM_RTC_CR_AF_V(v) BM_RTC_CR_AF
+#define BP_RTC_CR_AIE 3
+#define BM_RTC_CR_AIE 0x8
+#define BF_RTC_CR_AIE(v) (((v) & 0x1) << 3)
+#define BFM_RTC_CR_AIE(v) BM_RTC_CR_AIE
+#define BF_RTC_CR_AIE_V(e) BF_RTC_CR_AIE(BV_RTC_CR_AIE__##e)
+#define BFM_RTC_CR_AIE_V(v) BM_RTC_CR_AIE
+#define BP_RTC_CR_AE 2
+#define BM_RTC_CR_AE 0x4
+#define BF_RTC_CR_AE(v) (((v) & 0x1) << 2)
+#define BFM_RTC_CR_AE(v) BM_RTC_CR_AE
+#define BF_RTC_CR_AE_V(e) BF_RTC_CR_AE(BV_RTC_CR_AE__##e)
+#define BFM_RTC_CR_AE_V(v) BM_RTC_CR_AE
+#define BP_RTC_CR_SELEXC 1
+#define BM_RTC_CR_SELEXC 0x2
+#define BF_RTC_CR_SELEXC(v) (((v) & 0x1) << 1)
+#define BFM_RTC_CR_SELEXC(v) BM_RTC_CR_SELEXC
+#define BF_RTC_CR_SELEXC_V(e) BF_RTC_CR_SELEXC(BV_RTC_CR_SELEXC__##e)
+#define BFM_RTC_CR_SELEXC_V(v) BM_RTC_CR_SELEXC
+#define BP_RTC_CR_ENABLE 0
+#define BM_RTC_CR_ENABLE 0x1
+#define BF_RTC_CR_ENABLE(v) (((v) & 0x1) << 0)
+#define BFM_RTC_CR_ENABLE(v) BM_RTC_CR_ENABLE
+#define BF_RTC_CR_ENABLE_V(e) BF_RTC_CR_ENABLE(BV_RTC_CR_ENABLE__##e)
+#define BFM_RTC_CR_ENABLE_V(v) BM_RTC_CR_ENABLE
+
+#define REG_RTC_SR jz_reg(RTC_SR)
+#define JA_RTC_SR (0xb0003000 + 0x4)
+#define JT_RTC_SR JIO_32_RW
+#define JN_RTC_SR RTC_SR
+#define JI_RTC_SR
+
+#define REG_RTC_SAR jz_reg(RTC_SAR)
+#define JA_RTC_SAR (0xb0003000 + 0x8)
+#define JT_RTC_SAR JIO_32_RW
+#define JN_RTC_SAR RTC_SAR
+#define JI_RTC_SAR
+
+#define REG_RTC_GR jz_reg(RTC_GR)
+#define JA_RTC_GR (0xb0003000 + 0xc)
+#define JT_RTC_GR JIO_32_RW
+#define JN_RTC_GR RTC_GR
+#define JI_RTC_GR
+#define BP_RTC_GR_ADJC 16
+#define BM_RTC_GR_ADJC 0x3ff0000
+#define BF_RTC_GR_ADJC(v) (((v) & 0x3ff) << 16)
+#define BFM_RTC_GR_ADJC(v) BM_RTC_GR_ADJC
+#d