ODROID-M2: New support for ODROID-M2

Signed-off-by: Steve Jeong <steve@how2flow.net>
Change-Id: I379018ae50e6c2c43f0e53d9bc5986a2090d7818
diff --git a/Android.bp b/Android.bp
index 0c3c33c..7aa17ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,6 +28,7 @@
         "wiringPi/odroidhc4.c",
         "wiringPi/odroidm1.c",
         "wiringPi/odroidm1s.c",
+        "wiringPi/odroidm2.c",
         "wiringPi/drcSerial.c",
         "wiringPi/mcp23s08.c",
         "wiringPi/odroidn1.c",
diff --git a/gpio/readall.c b/gpio/readall.c
index c5d4fc8..515ed6a 100644
--- a/gpio/readall.c
+++ b/gpio/readall.c
@@ -67,7 +67,9 @@
 /*----------------------------------------------------------------------------*/
 static const char *alts [] =
 {
-	"IN", "OUT", "ALT1", "ALT2", "ALT3", "ALT4", "ALT5", "ALT6", "ALT7"
+	"IN", "OUT",
+	"ALT1", "ALT2", "ALT3", "ALT4", "ALT5", "ALT6", "ALT7",
+	"ALT8", "ALT9", "ALTa", "ALTb", "ALTc", "ALTd", "ALTe",
 } ;
 
 static const char *pupd [] =
@@ -714,6 +716,67 @@
 	NULL,NULL,NULL,
 };
 
+/*----------------------------------------------------------------------------*/
+static const char *physNamesOdroidM2All [64] =
+{
+	NULL,
+
+	"    3.3V", "5V      ",
+	"I2C0_SDA", "5V      ",
+	"I2C0_SCL", "GND(0V) ",
+	"GPIO0_D0", "UART1_TX",
+	" GND(0V)", "UART1_RX",
+	"GPIO3_D4", "GPIO3_B2",
+	"GPIO3_D5", "GND(0V) ",
+	"GPIO4_B3", "GPIO1_B2",
+	"    3.3V", "GPIO1_B3",
+	"SPI_MOSI", "GND(0V) ",
+	"SPI_MISO", "GPIO0_D4",
+	" SPI_CLK", "SPI_CS0 ",
+	" GND(0V)", "GPIO1_B4",
+	"I2C1_SDA", "I2C1_SCL",
+	"GPIO1_B7", "GND(0V) ",
+	"GPIO1_B6", "GPIO4_B4",
+	"GPIO3_D0", "GND(0V) ",
+	"GPIO3_C7", "GPIO4_B5",
+	"ADC.AIN4", "1V8     ",
+	" GND(0V)", "ADC.AIN5",
+
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,
+};
+
+/*----------------------------------------------------------------------------*/
+static const char *physNamesOdroidM2 [64] =
+{
+	NULL,
+
+	"   3.3V", "5V     ",
+	"   SDA0", "5V     ",
+	"   SCL0", "0V     ",
+	" IO0_D0", "UART.TX",
+	"     0V", "UART.RX",
+	" IO3_D4", "IO3_B2 ",
+	" IO3_D5", "0V     ",
+	" IO4_B3", "IO1_B2 ",
+	"   3.3V", "IO1_B3 ",
+	"SPI.TXD", "0V     ",
+	"SPI.RXD", "IO0_D4 ",
+	"SPI.CLK", "SPI.CS0",
+	"     0V", "IO1_B4 ",
+	"   SDA1", "SCL1   ",
+	" IO1_B7", "0V     ",
+	" IO1_B6", "IO4_B4 ",
+	" IO3_D0", "0V     ",
+	" IO3_C7", "IO4_B5 ",
+	"   AIN4", "1V8    ",
+	"     0V", "AIN5   ",
+
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,
+};
 
 /*----------------------------------------------------------------------------*/
 static void readallPhys(int model, int UNU rev, int physPin, const char *physNames[], int isAll) {
@@ -770,6 +833,7 @@
 			case MODEL_ODROID_C4:
 			case MODEL_ODROID_M1:
 			case MODEL_ODROID_M1S:
+			case MODEL_ODROID_M2:
 				printf (" | %2d | %5s", getDrive(pin), pupd[getPUPD(pin)]);
 				break;
 			default:
@@ -813,6 +877,7 @@
 			case MODEL_ODROID_C4:
 			case MODEL_ODROID_M1:
 			case MODEL_ODROID_M1S:
+			case MODEL_ODROID_M2:
 				printf (" | %-5s | %-2d", pupd[getPUPD(pin)], getDrive(pin));
 				break;
 			default:
@@ -1039,6 +1104,10 @@
 			headerName = (isAll == FALSE) ? "    M1S   " : "     Model ODROID-M1S     ";
 			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidM1S : physNamesOdroidM1SAll);
 			break;
+		case MODEL_ODROID_M2:
+			headerName = (isAll == FALSE) ? "--- M2 ---" : "---- Model ODROID-M2 ----";
+			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidM2 : physNamesOdroidM2All);
+			break;
 		default:
 			printf("Oops - unknown model: %d\n", model);
 			return;
diff --git a/udev/rules.d/99-odroid-wiringpi-rockchip.rules b/udev/rules.d/99-odroid-wiringpi-rockchip.rules
index c6f353d..d4cdf25 100644
--- a/udev/rules.d/99-odroid-wiringpi-rockchip.rules
+++ b/udev/rules.d/99-odroid-wiringpi-rockchip.rules
@@ -1,2 +1,3 @@
 # /dev/gpiomem
 SUBSYSTEM=="rk3568-gpiomem", GROUP="odroid", MODE="0660"
+SUBSYSTEM=="rk35xx-gpiomem", GROUP="odroid", MODE="0660"
diff --git a/wiringPi/Makefile.am b/wiringPi/Makefile.am
index 33de09a..35eb930 100644
--- a/wiringPi/Makefile.am
+++ b/wiringPi/Makefile.am
@@ -24,6 +24,7 @@
 	odroidhc4.c \
 	odroidm1.c \
 	odroidm1s.c \
+	odroidm2.c \
 	odroidn1.c \
 	odroidn2.c \
 	odroidxu3.c \
diff --git a/wiringPi/odroidm2.c b/wiringPi/odroidm2.c
new file mode 100644
index 0000000..a2c2079
--- /dev/null
+++ b/wiringPi/odroidm2.c
@@ -0,0 +1,1276 @@
+/*----------------------------------------------------------------------------*/
+//
+//
+//	WiringPi ODROID-M2 Board Control file (ROCKCHIP 64Bits Platform)
+//
+//
+/*----------------------------------------------------------------------------*/
+/*******************************************************************************
+Copyright (C) 2024 Steve Jeong <steve@how2flow.net>
+
+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 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*******************************************************************************/
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+/*----------------------------------------------------------------------------*/
+#include "softPwm.h"
+#include "softTone.h"
+/*----------------------------------------------------------------------------*/
+#include "wiringPi.h"
+#include "odroidm2.h"
+/*----------------------------------------------------------------------------*/
+// wiringPi gpio map define
+/*----------------------------------------------------------------------------*/
+static const int pinToGpio[64] = {
+	// wiringPi number to native gpio number
+	124,106,	//  0 |  1 : GPIO3_D4, GPIO3_B2
+	125,139,	//  2 |  3 : GPIO3_D5, GPIO4_B3
+	42,  43,	//  4 |  5 : GPIO1_B2, GPIO1_B3
+	28,  24,	//  6 |  7 : GPIO0_D4, GPIO0_D0
+	135,134,	//  8 |  9 : GPIO4_A7, GPIO4_A6
+	29,  44,	// 10 | 11 : GPIO0_D5, GPIO1_B4
+	122,121,	// 12 | 13 : GPIO3_D2, GPIO3_D1
+	123,112,	// 14 | 15 : GPIO3_D3, GPIO3_C0
+	113, -1,	// 16 | 17 : GPIO3_C1
+	-1,  -1,	// 18 | 19 :
+	-1,  47,	// 20 | 21 : , GPIO1_B7
+	46, 120,	// 22 | 23 : GPIO1_B6, GPIO3_D0
+	119, -1,	// 24 | 25 : GPIO3_C7,
+	140,141,	// 26 | 27 : GPIO4_B4, GPIO4_B5
+	-1,  -1,	// 28 | 29 :
+	136,137,	// 30 | 31 : GPIO4_B0, GPIO4_B1
+	// EXT_PINS:
+	116, 117, 118, -1, // 32...35
+	// Padding:
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	-1,// 36...49
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 50...63
+};
+
+static const int phyToGpio[64] = {
+	// physical header pin number to native gpio number
+	-1,		//  0
+	-1,  -1,	//  1 |  2 : 3.3V, 5.0V
+	135, -1,	//  3 |  4 : GPIO4_A7, 5.0V
+	134, -1,	//  5 |  6 : GPIO4_A6, GND
+	24, 112,	//  7 |  8 : GPIO0_D0, GPIO3_C0
+	-1, 113,	//  9 | 10 : GND, GPIO3_C1
+	124,106,	// 11 | 12 : GPIO3_D4, GPIO3_B2
+	125, -1,	// 13 | 14 : GPIO3_D5, GND
+	139, 42,	// 15 | 16 : GPIO4_B3, GPIO1_B2
+	-1,  43,	// 17 | 18 : 3.3V, GPIO1_B3
+	122, -1,	// 19 | 20 : GPIO3_D2, GND
+	121, 28,	// 21 | 22 : GPIO3_D1, GPIO0_D4
+	123, 29,	// 23 | 24 : GPIO3_D3, GPIO0_D5
+	-1,  44,	// 25 | 26 : GND, GPIO1_B4
+	136,137,	// 27 | 28 : GPIO4_B0, GPIO4_B1
+	47,  -1,	// 29 | 30 : GPIO1_B7, GND
+	46, 140,	// 31 | 32 : GPIO1_B6, GPIO4_B4
+	120, -1,	// 33 | 34 : GPIO3_D0, GND
+	119,141,	// 35 | 36 : GPIO3_C7, GPIO4_B5
+	-1,  -1,	// 37 | 38 : ADC.AIN4, 1.8V REF
+	-1,  -1,	// 39 | 40 : GND, ADC.AIN5
+
+	// Not used
+	-1, -1, -1, -1, -1, -1, -1, -1,	-1, -1, // 41...50
+
+	// EXT_PINS
+	116, 117, 118, // 51...53
+
+	// Not used
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1	// 54...63
+};
+
+static const char *pinToPwm[64] = {
+	// wiringPi number to pwm group number
+		"None", "fd8b0030",	   // GPIO3_D4, GPIO3_B2 (PWM3)
+		"None", "febf0030",	   // GPIO3_D5, GPIO4_B3 (PWM15)
+		"None", "None",		   // GPIO1_B2, GPIO1_B3
+		"None", "febd0030",	   // GPIO0_D4, GPIO0_D0 (PWM7)
+		"None", "None",        // GPIO4_A7, GPIO4_A6
+		"None", "None",        // GPIO0_D5, GPIO1_B4
+		"None", "None",        // GPIO3_D2, GPIO3_D1
+		"None", "None",        // GPIO3_D3, GPIO3_C0
+		"None", "None",		   // GPIO3_C1
+		"None", "None",		   //
+		"None", "None",		   // , GPIO1_B7
+		"None", "febe0000",	   // GPIO1_B6, GPIO3_D0 (PWM8)
+		"None", "None",		   // GPIO3_C7,
+		"None", "None",        // GPIO4_B4, GPIO4_B5
+		"None", "None",        //
+		"None", "None",        // GPIO4_B0, GPIO4_B1
+	// Padding:
+	"None","None","None","None","None","None","None","None","None","None","None","None","None","None","None","None", // 32...47
+	"None","None","None","None","None","None","None","None","None","None","None","None","None","None","None","None"  // 48...63
+};
+
+static const int pinToPwmNum[64] = {
+	// wiringPi number to pwm pin number
+	 -1,  3,	//  0 |  1 : GPIO3_D4, GPIO3_B2 (PWM3)
+	 -1,  2,	//  2 |  3 : GPIO3_D5, GPIO4_B3 (PWM15)
+	 -1, -1,	//  4 |  5 : GPIO1_B2, GPIO1_B3
+	 -1,  1,	//  6 |  7 : GPIO0_D4, GPIO0_D0 (PWM7)
+	 -1, -1,	//  8 |  9 : GPIO4_A7, GPIO4_A6
+	 -1, -1,	// 10 | 11 : GPIO0_D5, GPIO1_B4
+	 -1, -1,	// 12 | 13 : GPIO3_D2, GPIO3_D1
+	 -1, -1,	// 14 | 15 : GPIO3_D3, GPIO3_C0
+	 -1, -1,	// 16 | 17 : GPIO3_C1
+	 -1, -1,	// 18 | 19 :
+	 -1, -1,	// 20 | 21 : , GPIO1_B7
+	 -1,  0,	// 22 | 23 : GPIO1_B6, GPIO3_D0 (PWM8)
+	 -1, -1,	// 24 | 25 : GPIO3_C7,
+	 -1, -1,	// 26 | 27 : GPIO4_B4, GPIO4_B5
+	 -1, -1,	// 28 | 29 :
+	 -1, -1,	// 30 | 31 : GPIO4_B0, GPIO4_B1
+	// Padding:
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	// 32...47
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1	// 48...63
+};
+
+static char pwmPinPath[10][(BLOCK_SIZE)] = {
+	"","",
+	"","",
+	// Padding:
+	"None","None","None",
+	"None","None","None"
+};
+
+static char setupedPwmPinPath[10][BLOCK_SIZE] = {
+	"None","None",
+	"None","None",
+	"None","None",
+	"None","None",
+	"None","None"
+};
+/*----------------------------------------------------------------------------*/
+//
+// Global variable define
+//
+/*----------------------------------------------------------------------------*/
+/* ADC file descriptor */
+static int adcFds[2];
+
+/* GPIO mmap control. Actual GPIO bank number. */
+static volatile uint32_t *gpio[5];
+
+/* GRF(General Register Files) base addresses to control GPIO modes */
+static volatile uint32_t *grf[2];
+
+/* CRU(Clock & Reset Unit) base addresses to control CLK mode */
+static volatile uint32_t *cru[2];
+
+/* wiringPi Global library */
+static struct libodroid	*lib = NULL;
+
+/* pwm sysnode */
+static DIR *pwm;
+static struct dirent *pwmchip;
+/* pwm params */
+static char sysPwmPath[(BLOCK_SIZE / 4)];
+static char pwmExport[(BLOCK_SIZE / 16)];
+static char pwmUnexport[(BLOCK_SIZE / 16)];
+static char pwmPeriod[(BLOCK_SIZE / 16)];
+static char pwmDuty[(BLOCK_SIZE / 16)];
+static unsigned int pwmClock;
+static unsigned int pwmRange;
+/*----------------------------------------------------------------------------*/
+// Function prototype define
+/*----------------------------------------------------------------------------*/
+static int	gpioToShiftRegBy32	(int pin);
+static int	gpioToShiftRegBy16	(int pin);
+static void	setClkState	(int bank, int state);
+static int	setIomuxMode 	(int pin, int mode);
+/*----------------------------------------------------------------------------*/
+// Function of pwm define
+/*----------------------------------------------------------------------------*/
+static int	pinToSysPwmPath	(int pin);
+static int	pwmSetup (int pin);
+static int	pwmRelease (int pin);
+/*----------------------------------------------------------------------------*/
+// wiringPi core function
+/*----------------------------------------------------------------------------*/
+static int		_getModeToGpio		(int mode, int pin);
+static int		_pinMode		(int pin, int mode);
+static int		_getDrive		(int pin);
+static int		_setDrive		(int pin, int value);
+static int		_getAlt			(int pin);
+static int		_getPUPD		(int pin);
+static int		_pullUpDnControl	(int pin, int pud);
+static int		_digitalRead		(int pin);
+static int		_digitalWrite		(int pin, int value);
+static int		_pwmWrite		(int pin, int value);
+static int		_analogRead		(int pin);
+static int		_digitalWriteByte	(const unsigned int value);
+static unsigned int	_digitalReadByte	(void);
+static void		_pwmSetRange	(unsigned int range);
+static void		_pwmSetClock	(int divisor);
+/*----------------------------------------------------------------------------*/
+// board init function
+/*----------------------------------------------------------------------------*/
+static 	void init_gpio_mmap	(void);
+static 	void init_adc_fds	(void);
+void init_odroidm2 	(struct libodroid *libwiring);
+/*----------------------------------------------------------------------------*/
+//
+// for the offset to the GPIO bit
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToShiftRegBy32 (int pin)
+{
+	return pin % 32;
+}
+/*----------------------------------------------------------------------------*/
+//
+// for the offset to the GPIO bit
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToShiftRegBy16 (int pin)
+{
+	return pin % 16;
+}
+/*----------------------------------------------------------------------------*/
+//
+// config pwm sys path. "/sys/class/pwm/pwmchip?"
+//
+/*----------------------------------------------------------------------------*/
+static int pinToSysPwmPath (int pin)
+{
+	const char *pwmGroup;
+	char pwmLinkSrc[(BLOCK_SIZE / 8)];
+	char pwmPath[(BLOCK_SIZE / 8)];
+	int sz_link;
+
+	memset(pwmLinkSrc, 0, sizeof(pwmLinkSrc));
+	memset(pwmPath, 0, sizeof(pwmPath));
+
+	pwmGroup = pinToPwm[pin];
+	pwm = opendir("/sys/class/pwm");
+	if (pwm == NULL) {
+		printf("need to set device: pwm\n");
+		return -1;
+	}
+
+	while (1) {
+		pwmchip = readdir(pwm);
+
+		if (pwmchip == NULL) {
+			break;
+		}
+
+		if (strlen(pwmchip->d_name) <= 2)
+			continue;
+
+		sprintf(pwmPath, "%s/%s", "/sys/class/pwm", pwmchip->d_name);
+		sz_link = readlink(pwmPath, pwmLinkSrc, sizeof(pwmLinkSrc));
+		if (sz_link < 0) {
+			perror("Read symbolic link fail");
+			return sz_link;
+		}
+
+		if (strstr(pwmLinkSrc, pwmGroup) != NULL) {
+			strncpy(sysPwmPath, pwmPath, (sizeof(sysPwmPath) - 1));
+			break;
+		}
+	}
+	closedir(pwm);
+
+	return 0;
+}
+
+static int pwmSetup (int pin) {
+	char cmd[(BLOCK_SIZE * 2)];
+	int pwmPin, ret;
+
+	memset(cmd, 0, sizeof(cmd));
+	memset(pwmExport, 0, sizeof(pwmExport));
+
+	if ((ret = pinToSysPwmPath(pin)) < 0) {
+		perror("set pwm dtb overlays");
+		return ret;
+	}
+
+	if (strstr(sysPwmPath, "pwmchip") == NULL) {
+		printf("config pwm dtb overlays\n");
+		return -1;
+	}
+
+	pwmPin = pinToPwmNum[pin];
+	pwmClock = (M2_PWM_INTERNAL_CLK / 2);
+	sprintf(pwmExport, "%d", 0);
+	sprintf(pwmPinPath[pwmPin], "%s/pwm%d", sysPwmPath, 0);
+	strncpy(setupedPwmPinPath[pwmPin], pwmPinPath[pwmPin], (BLOCK_SIZE - 1));
+#ifdef ANDROID
+	sprintf(cmd, "su -s sh -c %s %s", SYS_ACCESS_SCRIPT, pwmPinPath[pwmPin]);
+#else
+	sprintf(cmd, "sudo sh %s %s", SYS_ACCESS_SCRIPT, pwmPinPath[pwmPin]);
+#endif
+	inputToSysNode(sysPwmPath, "export", pwmExport);
+	system(cmd);
+	printf("PWM/pin%d: Don't change to gpio mode with overlay registered.\n", pin);
+
+	return 0;
+}
+
+static int pwmRelease (int pin) {
+	int pwmPin, ret;
+
+	if ((ret = pinToSysPwmPath(pin)) < 0) {
+		return ret;
+	}
+
+	if (strstr(sysPwmPath, "pwmchip") == NULL) {
+		return -1;
+	}
+
+	pwmPin = pinToPwmNum[pin];
+	sprintf(pwmUnexport, "%d", (pwmPin % 2));
+	sprintf(pwmPinPath[pwmPin], "%s/pwm%d", sysPwmPath, (pwmPin % 2));
+	if ((pwm = opendir(pwmPinPath[pwmPin])) != NULL) {
+		inputToSysNode(pwmPinPath[pwmPin], "enable", "0");
+		inputToSysNode(sysPwmPath, "unexport", pwmUnexport);
+		closedir(pwm);
+	}
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _getModeToGpio (int mode, int pin)
+{
+	if (pin > 255)
+		return msg(MSG_ERR, "%s : Invalid pin number %d\n", __func__, pin);
+
+	switch (mode) {
+	/* Native gpio number */
+	case	MODE_GPIO:
+		return	pin;
+	/* Native gpio number for sysfs */
+	case	MODE_GPIO_SYS:
+		return	lib->sysFds[pin] != -1 ? pin : -1;
+	/* wiringPi number */
+	case	MODE_PINS:
+		return	pin < 64 ? pinToGpio[pin] : -1;
+	/* header pin number */
+	case	MODE_PHYS:
+		return	pin < 64 ? phyToGpio[pin] : -1;
+	default	:
+		msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+		return	-1;
+	}
+}
+/*----------------------------------------------------------------------------*/
+//
+// set GPIO clock state
+//
+/*----------------------------------------------------------------------------*/
+static void setClkState (int bank, int state)
+{
+	uint32_t data, regOffset;
+	uint8_t gpioPclkShift;
+
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = M2_PMU1CRU_GPIO_CLK_OFFSET;
+			gpioPclkShift = M2_PMU1CRU_GPIO_PCLK_BIT;
+			break;
+		case 1:
+			regOffset = M2_CRU_GPIO1_CLK_OFFSET;
+			gpioPclkShift = M2_CRU_GPIO1_PCLK_BIT;
+			break;
+		default:
+			regOffset = M2_CRU_GPIO_CLK_OFFSET;
+			gpioPclkShift = ((bank - 2) * 2);
+			break;
+	}
+
+	data = *(cru[(bank != 0)] + regOffset);
+	data &= ~(1 << gpioPclkShift);
+
+	if (state)
+		data |= (1 << gpioPclkShift);
+	data |= (1 << (gpioPclkShift + 16)); // write_mask
+
+	*(cru[(bank != 0)] + regOffset) = data;
+
+}
+/*----------------------------------------------------------------------------*/
+//
+// set IOMUX mode
+//
+/*----------------------------------------------------------------------------*/
+static int setIomuxMode (int pin, int mode)
+{
+	uint32_t data, regOffset;
+	uint8_t	bank, group, bankOffset, groupOffset;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8); // A or B or C or D
+	groupOffset = (pin % 8);
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = (pin < 12 ? M2_PMU1_IOC_OFFSET : M2_PMU2_IOC_OFFSET);
+			break;
+		default:
+			regOffset = M2_BUS_IOC_OFFSET;
+			break;
+	}
+
+	if (mode == M2_FUNC_PWM) {
+		bank = 1;
+		regOffset = M2_BUS_IOC_OFFSET;
+	}
+
+	regOffset += (bank * 0x8) + (group * 0x2);
+	regOffset += (groupOffset / 4 == 0) ? 0x0 : 0x1;
+	data = *(grf[(bank != 0)] + regOffset);
+
+	// Common IOMUX Funtion 1 : GPIO (4'h0)
+	switch (mode) {
+	case M2_FUNC_GPIO: // Common IOMUX Function 1_GPIO (4'h0)
+		data &= ~(0xf << ((groupOffset % 4) * 4)); // ~0x0f = 4'h0
+		data |= (0xf << ((groupOffset % 4) * 4 + 16)); // write_mask
+		*(grf[bank != 0] + regOffset) = data;
+		break;
+	case M2_FUNC_PWM: // gpio0_D0, gpio3_B2/D0, gpio4_B3: 4'h1011
+		data |= (0xb << ((groupOffset % 4) * 4));
+		data &= ~(0x4 << ((groupOffset % 4) * 4));
+		data |= (0xf << ((groupOffset % 4) * 4 + 16)); // write_mask
+		*(grf[bank] + regOffset) = data;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _pinMode (int pin, int mode)
+{
+	uint32_t data, regOffset;
+	uint8_t bank, bankOffset;
+	int origPin;
+
+	origPin = pin;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	regOffset = (bankOffset / 16 == 0 ? M2_GPIO_DIR_OFFSET : M2_GPIO_DIR_OFFSET + 0x1);
+
+	pwmRelease(origPin);
+	softPwmStop(origPin);
+	softToneStop(origPin);
+	setClkState(bank, M2_CLK_ENABLE);
+
+	data = *(gpio[bank] + regOffset);
+
+	switch (mode) {
+		case INPUT:
+		case INPUT_PULLUP:
+		case INPUT_PULLDOWN:
+			_pullUpDnControl(origPin, mode);
+			__attribute__((fallthrough));
+		case OUTPUT:
+			setIomuxMode(origPin, M2_FUNC_GPIO);
+			mode = (mode == OUTPUT);
+			data &= ~(1 << gpioToShiftRegBy16(pin));
+			data |=(mode << gpioToShiftRegBy16(pin));
+			data |= (1 << (gpioToShiftRegBy16(pin) + 16)); // write_mask
+			*(gpio[bank] + regOffset) = data;
+			break;
+		case SOFT_PWM_OUTPUT:
+			softPwmCreate(origPin, 0, 100);
+			break;
+		case SOFT_TONE_OUTPUT:
+			softToneCreate(origPin);
+			break;
+		case PWM_OUTPUT:
+			setIomuxMode(origPin, M2_FUNC_PWM);
+			pwmSetup(origPin);
+			break;
+		default:
+			msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+			break;
+	}
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _getDrive(int pin)
+{
+	uint32_t data, regOffset;
+	uint8_t bank, group, bankOffset, groupOffset;
+	int value = 0;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8);
+	groupOffset = (pin % 8);
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = M2_PMU1_IOC_DS_OFFSET;
+			if (pin >=12) {
+				regOffset = M2_PMU2_IOC_DS_OFFSET;
+			}
+			break;
+		case 1:
+			regOffset = M2_VCCIO1_4_IOC_DS_OFFSET;
+			break;
+		case 2:
+			regOffset = M2_VCCIO3_5_IOC_DS_OFFSET;
+			__attribute__((fallthrough));
+		case 3:
+			break;
+		case 4:
+			if (group < 3) {
+				regOffset = M2_VCCIO6_IOC_DS_OFFSET;
+				if (pin >= 146 && pin <= 150) {
+					regOffset = M2_VCCIO3_5_IOC_DS_OFFSET;
+				}
+			}
+			else {
+				regOffset = M2_VCCIO2_IOC_DS_OFFSET;
+			}
+			break;
+		default:
+			msg(MSG_WARN, "%s : Out of bank %d\n", __func__, bank);
+			break;
+	}
+
+	regOffset += (bank * 0x8) + (group * 0x2);
+	regOffset += (groupOffset / 4 == 0) ? 0x0 : 0x1;
+
+	data = *(grf[(bank != 0)] + regOffset);
+	data = (data >> ((groupOffset % 4) * 4)) & 0x7;
+
+	switch (data) {
+		case M2_DS_LEVEL_0:
+			value = 0;
+			break;
+		case M2_DS_LEVEL_1:
+			value = 1;
+			break;
+		case M2_DS_LEVEL_2:
+			value = 2;
+			break;
+		case M2_DS_LEVEL_3:
+			value = 3;
+			break;
+		case M2_DS_LEVEL_4:
+			value = 4;
+			break;
+		case M2_DS_LEVEL_5:
+			value = 5;
+			break;
+		default:
+			value = -1;
+			break;
+	}
+
+	return value;
+}
+/*----------------------------------------------------------------------------*/
+static int _setDrive(int pin, int value)
+{
+	uint32_t data, regOffset;
+	uint8_t bank, group, bankOffset, groupOffset;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8);
+	groupOffset = (pin % 8);
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = M2_PMU1_IOC_DS_OFFSET;
+			if (pin >=12) {
+				regOffset = M2_PMU2_IOC_DS_OFFSET;
+			}
+			break;
+		case 1:
+			regOffset = M2_VCCIO1_4_IOC_DS_OFFSET;
+			break;
+		case 2:
+			regOffset = M2_VCCIO3_5_IOC_DS_OFFSET;
+			__attribute__((fallthrough));
+		case 3:
+			break;
+		case 4:
+			if (group < 3) {
+				regOffset = M2_VCCIO6_IOC_DS_OFFSET;
+				if (pin >= 146 && pin <= 150) {
+					regOffset = M2_VCCIO3_5_IOC_DS_OFFSET;
+				}
+			}
+			else {
+				regOffset = M2_VCCIO2_IOC_DS_OFFSET;
+			}
+			break;
+		default:
+			msg(MSG_WARN, "%s : Out of bank %d\n", __func__, bank);
+			break;
+	}
+
+	regOffset += (bank * 0x8) + (group * 0x2);
+	regOffset += (groupOffset / 4 == 0) ? 0x0 : 0x1;
+
+	data = *(grf[(bank != 0)] + regOffset);
+	data |= (0x7777 << 16);
+	data = (data >> ((groupOffset % 4) * 4)) & 0x7;
+
+	switch (value) {
+		case 0:
+			data |= (M2_DS_LEVEL_0 << ((groupOffset % 4) * 4));
+			break;
+		case 1:
+			data |= (M2_DS_LEVEL_1 << ((groupOffset % 4) * 4));
+			break;
+		case 2:
+			data |= (M2_DS_LEVEL_2 << ((groupOffset % 4) * 4));
+			break;
+		case 3:
+			data |= (M2_DS_LEVEL_3 << ((groupOffset % 4) * 4));
+			break;
+		case 4:
+			data |= (M2_DS_LEVEL_4 << ((groupOffset % 4) * 4));
+			break;
+		case 5:
+			data |= (M2_DS_LEVEL_5 << ((groupOffset % 4) * 4));
+			break;
+		default:
+			break;
+	}
+
+	*(grf[(bank != 0)] + regOffset) = data;
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _getAlt (int pin)
+{
+	// TODO: Working confirmed
+	uint32_t regOffset;
+	uint16_t ret = 0;
+	uint8_t	bank, group, bankOffset, groupOffset, shift;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = (pin / GPIO_SIZE); // GPIO0, GPIO1, ...
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8); // GPIO0_A, GPIO0_B, ...
+	groupOffset = (pin % 8);
+	// The shift to move to the target pin at the register
+	shift = groupOffset % 4 * 4;
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = (pin < 12 ? M2_PMU1_IOC_OFFSET : M2_PMU2_IOC_OFFSET);
+			break;
+		default:
+			regOffset = M2_BUS_IOC_OFFSET;
+			break;
+	}
+
+	regOffset += (bank * 0x8) + (group * 0x2);
+	regOffset += (groupOffset / 4 == 0) ? 0x0 : 0x1;
+
+	ret = (*(grf[(bank != 0)] + regOffset) >> shift) & 0xf;
+
+	// If it is ALT0 (GPIO mode), check it's direction
+	// Add regOffset 0x4 to go to H register
+	// when the bit group is in the high two-bytes of the word size
+	if (ret == 0) {
+		if (bankOffset / 16 == 0)
+			regOffset = M2_GPIO_DIR_OFFSET;
+		else
+			regOffset = (M2_GPIO_DIR_OFFSET + 0x1);
+		ret = !!(*(gpio[bank] + regOffset) & (1 << gpioToShiftRegBy16(bankOffset)));
+	}
+	else {
+		// If it is alternative mode, add number 2 to fit into
+		// the alts[] array for "gpio readall" command
+		// Because the read number directly indicates the number of alt function
+		ret += 2;
+	}
+
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+static int _getPUPD (int pin)
+{
+	uint32_t regOffset, pupd;
+	uint8_t bank, group, bankOffset, groupOffset;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode,pin)) < 0)
+		return -1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8);
+	groupOffset = (pin % 8);
+	regOffset = 0;
+	pupd = (0x3 << (groupOffset * 2));
+
+	switch (bank) {
+		case 0:
+			regOffset = (pin < 12 ? M2_PMU1_IOC_PUPD_OFFSET : M2_PMU2_IOC_PUPD_OFFSET);
+			break;
+		case 1:
+			regOffset = M2_VCCIO1_4_IOC_PUPD_OFFSET;
+			break;
+		case 2:
+			__attribute__((fallthrough));
+		case 3:
+			regOffset = M2_VCCIO3_5_IOC_PUPD_OFFSET;
+			break;
+		case 4:
+			if (group < 3) {
+				regOffset = M2_VCCIO6_IOC_PUPD_OFFSET;
+				if (pin >= 146 && pin <= 150) {
+					regOffset = M2_VCCIO3_5_IOC_PUPD_OFFSET;
+				}
+			}
+			else {
+				regOffset = M2_VCCIO2_IOC_PUPD_OFFSET;
+			}
+			break;
+		default:
+			msg(MSG_WARN, "%s : Out of bank %d\n", __func__, bank);
+			break;
+	}
+
+	regOffset += (bank * 0x4) + group; // (group * 0x1)
+
+	pupd &= *(grf[(bank != 0)] + regOffset);
+	pupd = (pupd >> groupOffset * 2);
+
+	if (pupd & 1) {
+		pupd = (pupd & 2) ? 1 : 2;
+	}
+	else {
+		pupd = 0;
+	}
+
+	return pupd;
+}
+/*----------------------------------------------------------------------------*/
+static int _pullUpDnControl (int pin, int pud)
+{
+	uint32_t data, regOffset;
+	uint8_t	bank, group, bankOffset, groupOffset;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0) //exit
+		return	-1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	group = (bankOffset / 8); // A or B or C or D
+	groupOffset = (pin % 8);
+	regOffset = 0;
+
+	switch (bank) {
+		case 0:
+			regOffset = M2_PMU1_IOC_PUPD_OFFSET;
+			if (pin >=12) {
+				regOffset = M2_PMU2_IOC_PUPD_OFFSET;
+			}
+			break;
+		case 1:
+			regOffset = M2_VCCIO1_4_IOC_PUPD_OFFSET;
+			break;
+		case 2:
+			__attribute__((fallthrough));
+		case 3:
+			regOffset = M2_VCCIO3_5_IOC_PUPD_OFFSET;
+			break;
+		case 4:
+			if (group < 3) {
+				regOffset = M2_VCCIO6_IOC_PUPD_OFFSET;
+				if (pin >= 146 && pin <= 150) {
+					regOffset = M2_VCCIO3_5_IOC_PUPD_OFFSET;
+				}
+			}
+			else {
+				regOffset = M2_VCCIO2_IOC_PUPD_OFFSET;
+			}
+			break;
+		default:
+			msg(MSG_WARN, "%s : Out of bank %d\n", __func__, bank);
+			break;
+	}
+
+	regOffset += (bank * 0x4) + group; // (group * 0x1)
+
+	data = *(grf[(bank != 0)] + regOffset);
+	data &= ~(0x3 << (groupOffset * 2));
+
+	switch (pud) {
+	case PUD_UP:
+		data |= (0x3 << (groupOffset * 2));
+		break;
+	case PUD_DOWN:
+		data |= (0x1 << (groupOffset * 2));
+		break;
+	case PUD_OFF:
+		break;
+	default:
+		/* No message */
+		break;
+	}
+
+	data |= (0x3 << ((groupOffset * 2) + 16)); // write_mask
+	*(grf[(bank != 0)] + regOffset) = data;
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _digitalRead (int pin)
+{
+	uint8_t bank;
+	int ret;
+	char c;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		if (lib->sysFds[pin] == -1)
+			return -1;
+
+		lseek(lib->sysFds[pin], 0L, SEEK_SET);
+		if (read(lib->sysFds[pin], &c, 1) < 0) {
+			msg(MSG_WARN, "%s: Failed with reading from sysfs GPIO node. \n", __func__);
+			return -1;
+		}
+
+		return (c == '0') ? LOW : HIGH;
+	}
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = (pin / GPIO_SIZE);
+
+	ret = *(gpio[bank] + M2_GPIO_GET_OFFSET) & (1 << gpioToShiftRegBy32(pin)) ? HIGH : LOW;
+
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+static int _digitalWrite (int pin, int value)
+{
+	uint32_t data, regOffset;
+	uint8_t bank, bankOffset;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		if (lib->sysFds[pin] != -1) {
+			if (value == LOW) {
+				if (write (lib->sysFds[pin], "0\n", 2) < 0)
+					msg(MSG_ERR,
+					"%s : %s\nEdit direction file to output mode for\n\t/sys/class/gpio/gpio%d/direction\n",
+					__func__, strerror(errno), pin + M2_GPIO_PIN_BASE);
+			} else {
+				if (write (lib->sysFds[pin], "1\n", 2) < 0)
+					msg(MSG_ERR,
+					"%s : %s\nEdit direction file to output mode for\n\t/sys/class/gpio/gpio%d/direction\n",
+					__func__, strerror(errno), pin + M2_GPIO_PIN_BASE);
+			}
+		}
+		return -1;
+	}
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = (pin / GPIO_SIZE);
+	bankOffset = (pin - (bank * GPIO_SIZE));
+	regOffset = (bankOffset / 16 == 0 ? M2_GPIO_SET_OFFSET : M2_GPIO_SET_OFFSET + 0x01);
+
+	data = *(gpio[bank] + regOffset);
+	data &= ~(1 << gpioToShiftRegBy16(pin));
+	data |= (value << gpioToShiftRegBy16(pin));
+	data |= (1 << (gpioToShiftRegBy16(pin) + 16)); // write_mask
+	*(gpio[bank] + regOffset) = data;
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _pwmWrite (int pin, int value)
+{
+	unsigned int duty;
+	int pwmPin;
+
+	memset(pwmDuty, 0, sizeof(pwmDuty));
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if (((unsigned int)value > pwmRange) || (pwmRange <= 0)) {
+		printf("warn : pwm range value is greater than or equal pwmWrite's\n");
+		return -1;
+	}
+
+	pwmPin = pinToPwmNum[pin];
+	duty = ((value * 100) / pwmRange);
+	sprintf(pwmDuty, "%d", ((atoi(pwmPeriod) * duty) / 100));
+
+	inputToSysNode(pwmPinPath[pwmPin], "duty_cycle", pwmDuty);
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int _analogRead (int pin)
+{
+	char value[5] = {0, };
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	/* wiringPi ADC number = pin 25, pin 29 */
+	switch (pin) {
+#if defined(ARDUINO)
+	/* To work with physical analog channel numbering */
+	case	1:	case	25:
+		pin = 0;
+	break;
+	case	0:	case	29:
+		pin = 1;
+	break;
+#else
+	case	0:	case	25:
+		pin = 0;
+	break;
+	case	1:	case	29:
+		pin = 1;
+	break;
+#endif
+	default:
+		return	0;
+	}
+	if (adcFds [pin] == -1)
+		return 0;
+
+	lseek(adcFds [pin], 0L, SEEK_SET);
+	if (read(adcFds [pin], &value[0], 4) < 0) {
+		msg(MSG_WARN, "%s: Error occurs when it reads from ADC file descriptor. \n", __func__);
+		return -1;
+	}
+
+	return	atoi(value);
+}
+/*----------------------------------------------------------------------------*/
+static int _digitalWriteByte (const unsigned int value)
+{
+	union reg_bitfield gpio0, gpio1, gpio3, gpio4;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return -1;
+	}
+
+	setClkState(GPIO_SIZE * 0, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 1, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 3, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 4, M2_CLK_ENABLE);
+
+	/* Read data register */
+	gpio0.wvalue = *(gpio[0] + M2_GPIO_GET_OFFSET);
+	gpio1.wvalue = *(gpio[1] + M2_GPIO_GET_OFFSET);
+	gpio3.wvalue = *(gpio[3] + M2_GPIO_GET_OFFSET);
+	gpio4.wvalue = *(gpio[4] + M2_GPIO_GET_OFFSET);
+
+	/* Wiring PI GPIO0 = M2 GPIO3_D.4 */
+	gpio3.bits.bit28 = ((value & 0x01) >> 0);
+	/* Wiring PI GPIO1 = M2 GPIO3_B.2 */
+	gpio3.bits.bit10 = ((value & 0x02) >> 1);
+	/* Wiring PI GPIO2 = M2 GPIO3_D.5 */
+	gpio3.bits.bit29 = ((value & 0x04) >> 2);
+	/* Wiring PI GPIO3 = M2 GPIO4_B.3 */
+	gpio4.bits.bit11 = ((value & 0x08) >> 3);
+	/* Wiring PI GPIO4 = M2 GPIO1_B.2 */
+	gpio1.bits.bit10 = ((value & 0x10) >> 4);
+	/* Wiring PI GPIO5 = M2 GPIO1_B.3 */
+	gpio1.bits.bit11 = ((value & 0x20) >> 5);
+	/* Wiring PI GPIO6 = M1 GPIO0_D.4 */
+	gpio0.bits.bit28 = ((value & 0x40) >> 6);
+	/* Wiring PI GPIO7 = M1 GPIO0_D.0 */
+	gpio0.bits.bit24 = ((value & 0x80) >> 7);
+
+	/* Update data register */
+	*(gpio[0] + (M2_GPIO_SET_OFFSET + 0x1)) = (M2_WRITE_BYTE_MASK_GPIO0_H | (gpio0.wvalue >> 16));
+	*(gpio[1] + M2_GPIO_SET_OFFSET) = (M2_WRITE_BYTE_MASK_GPIO1_L | (gpio1.wvalue & 0xffff));
+	*(gpio[3] + (M2_GPIO_SET_OFFSET + 0x1)) = (M2_WRITE_BYTE_MASK_GPIO3_H | (gpio3.wvalue >> 16));
+	*(gpio[3] + M2_GPIO_SET_OFFSET) = (M2_WRITE_BYTE_MASK_GPIO3_L | (gpio3.wvalue & 0xffff));
+	*(gpio[4] + M2_GPIO_SET_OFFSET) = (M2_WRITE_BYTE_MASK_GPIO4_L | (gpio4.wvalue & 0xffff));
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+static unsigned int _digitalReadByte (void)
+{
+	union reg_bitfield gpio0, gpio1, gpio3, gpio4;
+
+	unsigned int value = 0;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return	-1;
+	}
+
+	setClkState(GPIO_SIZE * 0, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 1, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 3, M2_CLK_ENABLE);
+	setClkState(GPIO_SIZE * 4, M2_CLK_ENABLE);
+
+	/* Read data register */
+	gpio0.wvalue = *(gpio[0] + M2_GPIO_GET_OFFSET);
+	gpio1.wvalue = *(gpio[1] + M2_GPIO_GET_OFFSET);
+	gpio3.wvalue = *(gpio[3] + M2_GPIO_GET_OFFSET);
+	gpio4.wvalue = *(gpio[4] + M2_GPIO_GET_OFFSET);
+
+	/* Wiring PI GPIO0 = M2 GPIO3_D.4 */
+	if (gpio3.bits.bit28)
+		value |= 0x01;
+	/* Wiring PI GPIO1 = M2 GPIO3_B.2 */
+	if (gpio3.bits.bit10)
+		value |= 0x02;
+	/* Wiring PI GPIO2 = M2 GPIO3_D.5 */
+	if (gpio3.bits.bit29)
+		value |= 0x04;
+	/* Wiring PI GPIO3 = M2 GPIO4_B.3 */
+	if (gpio4.bits.bit11)
+		value |= 0x08;
+	/* Wiring PI GPIO4 = M2 GPIO1_B.2 */
+	if (gpio1.bits.bit10)
+		value |= 0x10;
+	/* Wiring PI GPIO5 = M2 GPIO1_B.3 */
+	if (gpio1.bits.bit11)
+		value |= 0x20;
+	/* Wiring PI GPIO6 = M1 GPIO0_D.4 */
+	if (gpio0.bits.bit28)
+		value |= 0x40;
+	/* Wiring PI GPIO7 = M1 GPIO0_D.0 */
+	if (gpio0.bits.bit24)
+		value |= 0x80;
+
+	return value;
+}
+/*----------------------------------------------------------------------------*/
+// PWM signal ___-----------___________---------------_______-----_
+//               <--value-->           <----value---->
+//               <-------range--------><-------range-------->
+// PWM frequency == (PWM clock) / range
+/*----------------------------------------------------------------------------*/
+static void _pwmSetRange (unsigned int range)
+{
+	unsigned int freq, period;
+
+	memset(pwmPeriod, 0, sizeof(pwmPeriod));
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return;
+
+	if (pwmClock < 2) {
+		printf("error : pwm freq: %dMHz / (pwmSetClock's value) >= 2\n",
+				(M2_PWM_INTERNAL_CLK / 2000000));
+		return;
+	}
+
+	pwmRange = range;
+	if ((pwmRange < 1) || (pwmRange >= pwmClock)) {
+		printf("error : invalid value. ( < pwm freq)\n");
+		return;
+	}
+
+	freq = (pwmClock / pwmRange);
+	period = (1000000000 / freq); // period: s to ns.
+	sprintf(pwmPeriod, "%d", period);
+
+	for (int i = 0; i < 10; i++) {
+		if (strstr(setupedPwmPinPath[i], "None") != NULL)
+			continue;
+		inputToSysNode(setupedPwmPinPath[i], "period", pwmPeriod);
+		inputToSysNode(setupedPwmPinPath[i], "polarity", "normal");
+		inputToSysNode(setupedPwmPinPath[i], "enable", "1");
+	}
+}
+/*----------------------------------------------------------------------------*/
+// Internal clock: 12MHz
+// PWM clock == (Internal clock) / divisor
+// PWM frequency == (PWM clock) / range
+/*----------------------------------------------------------------------------*/
+static void _pwmSetClock (int divisor)
+{
+	if (pwmClock > 0)
+		pwmClock = (pwmClock / divisor);
+	else {
+		printf("error : pwm mode error\n");
+		return;
+	}
+}
+/*----------------------------------------------------------------------------*/
+static void init_gpio_mmap (void)
+{
+	int fd = -1;
+	void *mapped_cru[2], *mapped_grf[2], *mapped_gpio[5];
+
+	/* GPIO mmap setup */
+	if (!getuid()) {
+		if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0)
+			msg (MSG_ERR,
+				"wiringPiSetup: Unable to open /dev/mem: %s\n",
+				strerror (errno));
+	} else {
+		if (access("/dev/gpiomem",0) == 0) {
+			if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0)
+				msg (MSG_ERR,
+					"wiringPiSetup: Unable to open /dev/gpiomem: %s\n",
+					strerror (errno));
+			setUsingGpiomem(TRUE);
+		} else
+			msg (MSG_ERR,
+				"wiringPiSetup: /dev/gpiomem doesn't exist. Please try again with sudo.\n");
+	}
+
+	if (fd < 0) {
+		msg(MSG_ERR, "wiringPiSetup: Cannot open memory area for GPIO use. \n");
+	} else {
+		mapped_cru[0] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_PMU1CRU_BASE);
+		mapped_cru[1] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_CRU_BASE);
+
+		mapped_grf[0] = mmap(0, M2_GRF_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_PMU_IOC_BASE);
+		mapped_grf[1] = mmap(0, M2_GRF_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_BUS_IOC_BASE);
+
+		mapped_gpio[0] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_GPIO_0_BASE);
+		mapped_gpio[1] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_GPIO_1_BASE);
+		mapped_gpio[2] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_GPIO_2_BASE);
+		mapped_gpio[3] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_GPIO_3_BASE);
+		mapped_gpio[4] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M2_GPIO_4_BASE);
+
+		if ((mapped_cru[0] == MAP_FAILED) || (mapped_cru[1] == MAP_FAILED)) {
+			msg (MSG_ERR,"wiringPiSetup: mmap (CRU) failed: %s\n",strerror (errno));
+		} else {
+			cru[0] = (uint32_t *) mapped_cru[0];
+			cru[1] = (uint32_t *) mapped_cru[1];
+		}
+
+		if ((mapped_grf[0] == MAP_FAILED) || (mapped_grf[1] == MAP_FAILED)) {
+			msg (MSG_ERR,"wiringPiSetup: mmap (GRF) failed: %s\n",strerror (errno));
+		} else {
+			grf[0] = (uint32_t *) mapped_grf[0];
+			grf[1] = (uint32_t *) mapped_grf[1];
+		}
+
+		if ((mapped_gpio[0] == MAP_FAILED) ||
+			(mapped_gpio[1] == MAP_FAILED) ||
+			(mapped_gpio[2] == MAP_FAILED) ||
+			(mapped_gpio[3] == MAP_FAILED) ||
+			(mapped_gpio[4] == MAP_FAILED)) {
+			msg (MSG_ERR,
+				"wiringPiSetup: mmap (GPIO) failed: %s\n",
+				strerror (errno));
+		} else {
+			gpio[0] = (uint32_t *) mapped_gpio[0];
+			gpio[1] = (uint32_t *) mapped_gpio[1];
+			gpio[2] = (uint32_t *) mapped_gpio[2];
+			gpio[3] = (uint32_t *) mapped_gpio[3];
+			gpio[4] = (uint32_t *) mapped_gpio[4];
+		}
+	}
+}
+/*----------------------------------------------------------------------------*/
+static void init_adc_fds (void)
+{
+	const char *AIN0_NODE, *AIN1_NODE;
+
+	AIN0_NODE = "/sys/devices/platform/fec10000.saradc/iio:device0/in_voltage5_raw";
+	AIN1_NODE = "/sys/devices/platform/fec10000.saradc/iio:device0/in_voltage4_raw";
+
+	adcFds[0] = open(AIN0_NODE, O_RDONLY);
+	adcFds[1] = open(AIN1_NODE, O_RDONLY);
+}
+/*----------------------------------------------------------------------------*/
+void init_odroidm2 (struct libodroid *libwiring)
+{
+	init_gpio_mmap();
+
+	init_adc_fds();
+
+	/* wiringPi Core function initialize */
+	libwiring->getModeToGpio	= _getModeToGpio;
+	libwiring->pinMode		= _pinMode;
+	libwiring->getAlt		= _getAlt;
+	libwiring->getPUPD		= _getPUPD;
+	libwiring->pullUpDnControl	= _pullUpDnControl;
+	libwiring->getDrive			= _getDrive;
+	libwiring->setDrive			= _setDrive;
+	libwiring->digitalRead		= _digitalRead;
+	libwiring->digitalWrite		= _digitalWrite;
+	libwiring->analogRead		= _analogRead;
+	libwiring->digitalWriteByte	= _digitalWriteByte;
+	libwiring->digitalReadByte	= _digitalReadByte;
+	libwiring->pwmWrite			= _pwmWrite;
+	libwiring->pwmSetRange		= _pwmSetRange;
+	libwiring->pwmSetClock		= _pwmSetClock;
+
+	/* specify pin base number */
+	libwiring->pinBase		= M2_GPIO_PIN_BASE;
+
+	/* global variable setup */
+	lib = libwiring;
+}
+/*----------------------------------------------------------------------------*/
diff --git a/wiringPi/odroidm2.h b/wiringPi/odroidm2.h
new file mode 100644
index 0000000..3769960
--- /dev/null
+++ b/wiringPi/odroidm2.h
@@ -0,0 +1,121 @@
+/*----------------------------------------------------------------------------*/
+/*
+
+	WiringPi ODROID-M2 Board Header file
+
+ */
+/*----------------------------------------------------------------------------*/
+/*******************************************************************************
+Copyright (C) 2024 Steve Jeong <steve@how2flow.net>
+
+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 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*******************************************************************************/
+
+#ifndef	__ODROID_M2_H__
+#define	__ODROID_M2_H__
+// flag of Using "/dev/gpiomem" or "libgpiod"
+#define DEVMEM
+
+/*----------------------------------------------------------------------------*/
+/* Common mmap block size for ODROID-M2 GRF register */
+#define M2_GPIO_PIN_BASE	0
+#define M2_GRF_BLOCK_SIZE 0xFFFF
+#define GPIO_SIZE	32
+
+/* setClkState mode */
+#define M2_CLK_ENABLE	0
+#define M2_CLK_DISABLE	1
+
+#define M2_FUNC_GPIO 0
+#define M2_FUNC_PWM 1
+
+/* CLK (Used for pmu system) */
+#define M2_PMU1CRU_BASE 0xFD7F0000
+#define M2_PMU1CRU_GPIO_CLK_OFFSET (0x814 >> 2)
+#define M2_PMU1CRU_GPIO_PCLK_BIT 5
+/* CLK (Used for always on system) */
+#define M2_CRU_BASE	0xFD7C0000
+#define M2_CRU_GPIO1_CLK_OFFSET (0x840 >> 2)
+#define M2_CRU_GPIO1_PCLK_BIT 14
+#define M2_CRU_GPIO_CLK_OFFSET (M2_CRU_GPIO1_CLK_OFFSET + 0x1)
+
+/* IOMUX GPIO0_A0:GPIO0_B7 PMU1: A0 ~ B3 PMU2(+0x4000): B4~D7 */
+#define M2_PMU_IOC_BASE 0xFD5F0000
+#define M2_PMU1_IOC_OFFSET 0x0
+#define M2_PMU2_IOC_OFFSET (0x3FF4 >> 2) // (0x4000 - 0xC)
+#define M2_PMU1_IOC_PUPD_OFFSET	(0x20 >> 2)
+#define M2_PMU1_IOC_DS_OFFSET	(0x10 >> 2)
+#define M2_PMU2_IOC_PUPD_OFFSET	(0x4024 >> 2) // 0x28 -> 0x24 to unify the operations
+#define M2_PMU2_IOC_DS_OFFSET	(0x4008 >> 2) // 0x14 -> 0x08 to unify the operations
+/* IOMUX GPIO1_A0:GPIO4_D7 (GPIO) */
+#define M2_BUS_IOC_BASE 0xFD5F8000
+#define M2_BUS_IOC_OFFSET 0x0
+/* GPIO1_A0:GPIO1_D7 (PUPD/DS) */
+#define M2_VCCIO1_4_IOC_PUPD_OFFSET (0x1100 >> 2) // 0x110 -> 0x100 to unify the operations
+#define M2_VCCIO1_4_IOC_DS_OFFSET (0x1000 >> 2) // 0x20 -> 0x00 to unify the operations
+/* GPIO2_A4:GPIO3_D7, GPIO4_C2:GPIO4_C6 (PUPD/DS) */
+#define M2_VCCIO3_5_IOC_PUPD_OFFSET (0x2100 >> 2) // 0x120 -> 0x100
+#define M2_VCCIO3_5_IOC_DS_OFFSET (0x2000 >> 2) // 0x44 -> 0x00 to unify the operations
+/* GPIO4_D0:GPIO4_D7 (PUPD/DS) */
+#define M2_VCCIO2_IOC_PUPD_OFFSET (0x3100 >> 2) // 0x14c -> 0x140 to unify the operations
+#define M2_VCCIO2_IOC_DS_OFFSET (0x3018 >> 2) // 0x98 -> 0x18 to unify the operations
+/* GPIO4_A0:GPIO4_C1 (PUPD/DS) */
+#define M2_VCCIO6_IOC_PUPD_OFFSET (0x4130 >> 2) // 0x140 -> 0x130
+#define M2_VCCIO6_IOC_DS_OFFSET (0x4000 >> 2) // 0x80 -> 0x00 to unify the operations
+
+/* GPIO[0] */
+#define M2_GPIO_0_BASE	0xFD8A0000
+/* GPIO[1:4] */
+#define M2_GPIO_1_BASE	0xFEC20000
+#define M2_GPIO_2_BASE	0xFEC30000
+#define M2_GPIO_3_BASE	0xFEC40000
+#define M2_GPIO_4_BASE	0xFEC50000
+
+/* GPIO Port Data common offset ([L]:A0~B7, [H]:C0~D7) */
+#define M2_GPIO_DIR_OFFSET (0x8 >> 2)
+#define M2_GPIO_SET_OFFSET 0x0
+#define M2_GPIO_GET_OFFSET (0x70 >> 2)
+
+/* GPIO DS LEVELS */
+#define M2_DS_LEVEL_0	0b000
+#define M2_DS_LEVEL_1	0b100
+#define M2_DS_LEVEL_2	0b010
+#define M2_DS_LEVEL_3	0b110
+#define M2_DS_LEVEL_4	0b001
+#define M2_DS_LEVEL_5	0b101
+
+/* GPIO write mask for WriteByte */
+#define M2_WRITE_BYTE_MASK_GPIO0_H 0x11000000
+#define M2_WRITE_BYTE_MASK_GPIO1_L 0x0C000000
+#define M2_WRITE_BYTE_MASK_GPIO3_H 0x30000000
+#define M2_WRITE_BYTE_MASK_GPIO3_L 0x04000000
+#define M2_WRITE_BYTE_MASK_GPIO4_L 0x08000000
+
+#define CONSUMER "consumer"
+
+#define M2_PWM_INTERNAL_CLK			24000000 // 24MHz
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_odroidm2 (struct libodroid *libwiring);
+
+#ifdef __cplusplus
+}
+#endif
+/*----------------------------------------------------------------------------*/
+#endif	/* __ODROID_M2_H__ */
+/*----------------------------------------------------------------------------*/
+
diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c
index d7bdbf3..6737de5 100644
--- a/wiringPi/wiringPi.c
+++ b/wiringPi/wiringPi.c
@@ -46,6 +46,7 @@
 #include "odroidn2.h"
 #include "odroidc4.h"
 #include "odroidm1.h"
+#include "odroidm2.h"
 
 /*----------------------------------------------------------------------------*/
 // Const string define
@@ -65,6 +66,7 @@
 	"ODROID-HC4",
 	"ODROID-M1",
 	"ODROID-M1S",
+	"ODROID-M2",
 };
 
 const char *piRevisionNames [16] =
@@ -544,6 +546,11 @@
 			libwiring.mem = 5;
 			libwiring.rev = 1;
 			break;
+		case MODEL_ODROID_M2:
+			libwiring.maker = MAKER_ROCKCHIP;
+			libwiring.mem = 5;
+			libwiring.rev = 1;
+			break;
 		case MODEL_UNKNOWN:
 		default:
 			libwiring.model = MAKER_UNKNOWN;
@@ -1269,6 +1276,9 @@
 	case MODEL_ODROID_M1S:
 		init_odroidm1s(&libwiring);
 	break;
+	case MODEL_ODROID_M2:
+		init_odroidm2(&libwiring);
+	break;
 	default:
 		return wiringPiFailure (WPI_ALMOST,
 			"wiringPiSetup: Unknown model\n");
diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h
index eb51541..067b543 100644
--- a/wiringPi/wiringPi.h
+++ b/wiringPi/wiringPi.h
@@ -62,6 +62,7 @@
 #define	MODEL_ODROID_HC4	7
 #define	MODEL_ODROID_M1		8
 #define	MODEL_ODROID_M1S	9
+#define	MODEL_ODROID_M2		10
 
 #define	MAKER_UNKNOWN		0
 #define	MAKER_AMLOGIC		1
diff --git a/wiringPi/wiringPiI2C.c b/wiringPi/wiringPiI2C.c
index 14bc5f8..7a843e5 100644
--- a/wiringPi/wiringPiI2C.c
+++ b/wiringPi/wiringPiI2C.c
@@ -268,6 +268,7 @@
 			device = "/dev/i2c-2";
 	break;
 	case MODEL_ODROID_M1S:
+	case MODEL_ODROID_M2:
 		device = "/dev/i2c-0";
 	break;
 	}
diff --git a/wiringPi/wiringPiSPI.c b/wiringPi/wiringPiSPI.c
index 9730c50..9c1c4ef 100644
--- a/wiringPi/wiringPiSPI.c
+++ b/wiringPi/wiringPiSPI.c
@@ -150,6 +150,7 @@
 	case MODEL_ODROID_C4:
 	case MODEL_ODROID_M1:
 	case MODEL_ODROID_M1S:
+	case MODEL_ODROID_M2:
 		sprintf(device, "%s%d", spiDevType0, channel);
 	break;
 	case MODEL_ODROID_XU3: