ODROID-M1S: New support for ODROID-M1S

Signed-off-by: Steve Jeong <steve@how2flow.net>
Change-Id: Ib134094f7a35298b1274a60fbfc56a07a14ed965
diff --git a/Android.bp b/Android.bp
index 437a84b..a0adeb6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
         "wiringPi/odroidc4.c",
         "wiringPi/odroidhc4.c",
         "wiringPi/odroidm1.c",
+        "wiringPi/odroidm1s.c",
         "wiringPi/drcSerial.c",
         "wiringPi/mcp23s08.c",
         "wiringPi/odroidn1.c",
diff --git a/gpio/readall.c b/gpio/readall.c
index 97cb786..c5d4fc8 100644
--- a/gpio/readall.c
+++ b/gpio/readall.c
@@ -653,6 +653,69 @@
 };
 
 /*----------------------------------------------------------------------------*/
+static const char *physNamesOdroidM1SAll [64] =
+{
+	NULL,
+
+	"    3.3V", "5V      ",
+	"I2C0_SDA", "5V      ",
+	"I2C0_SCL", "GND(0V) ",
+	"GPIO0_B6", "UART1_TX",
+	" GND(0V)", "UART1_RX",
+	"GPIO0_C0", "GPIO2_A7",
+	"GPIO0_C1", "GND(0V) ",
+	"GPIO0_C2", "GPIO2_B5",
+	"    3.3V", "GPIO2_B6",
+	"SPI_MOSI", "GND(0V) ",
+	"SPI_MISO", "GPIO2_B0",
+	" SPI_CLK", "SPI_CS0 ",
+	" GND(0V)", "GPIO2_B1",
+	"I2C1_SDA", "I2C1_SCL",
+	"GPIO2_C0", "GND(0V) ",
+	"GPIO2_B7", "GPIO2_B2",
+	"GPIO0_B5", "GND(0V) ",
+	"GPIO2_A5", "GPIO2_A6",
+	"ADC.AIN1", "1V8     ",
+	" GND(0V)", "ADC.AIN0",
+
+	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 *physNamesOdroidM1S [64] =
+{
+	NULL,
+
+	"   3.3V", "5V     ",
+	"   SDA0", "5V     ",
+	"   SCL0", "0V     ",
+	" IO0_B6", "UART.TX",
+	"     0V", "UART.RX",
+	" IO0_C0", "IO2_A7 ",
+	" IO0_C1", "0V     ",
+	" IO0_C2", "IO2_B5 ",
+	"   3.3V", "IO2_B6 ",
+	"SPI.TXD", "0V     ",
+	"SPI.RXD", "IO2_B0 ",
+	"SPI.CLK", "SPI.CS0",
+	"     0V", "IO2_B1 ",
+	"   SDA1", "SCL1   ",
+	" IO2_C0", "0V     ",
+	" IO2_B7", "IO2_B2 ",
+	" IO0_B5", "0V     ",
+	" IO2_A5", "IO2_A6 ",
+	"   AIN1", "1V8    ",
+	"     0V", "AIN0   ",
+
+	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) {
 	int pin ;
 
@@ -706,6 +769,7 @@
 			case MODEL_ODROID_N2:
 			case MODEL_ODROID_C4:
 			case MODEL_ODROID_M1:
+			case MODEL_ODROID_M1S:
 				printf (" | %2d | %5s", getDrive(pin), pupd[getPUPD(pin)]);
 				break;
 			default:
@@ -748,6 +812,7 @@
 			case MODEL_ODROID_N2:
 			case MODEL_ODROID_C4:
 			case MODEL_ODROID_M1:
+			case MODEL_ODROID_M1S:
 				printf (" | %-5s | %-2d", pupd[getPUPD(pin)], getDrive(pin));
 				break;
 			default:
@@ -970,6 +1035,10 @@
 			headerName = (isAll == FALSE) ? "--- M1 ---" : "---- Model  ODROID-M1 ----";
 			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidM1 : physNamesOdroidM1All);
 			break;
+		case MODEL_ODROID_M1S:
+			headerName = (isAll == FALSE) ? "    M1S   " : "     Model ODROID-M1S     ";
+			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidM1S : physNamesOdroidM1SAll);
+			break;
 		default:
 			printf("Oops - unknown model: %d\n", model);
 			return;
diff --git a/wiringPi/Makefile.am b/wiringPi/Makefile.am
index aa0df83..33de09a 100644
--- a/wiringPi/Makefile.am
+++ b/wiringPi/Makefile.am
@@ -23,6 +23,7 @@
 	odroidc4.c \
 	odroidhc4.c \
 	odroidm1.c \
+	odroidm1s.c \
 	odroidn1.c \
 	odroidn2.c \
 	odroidxu3.c \
diff --git a/wiringPi/odroidm1.h b/wiringPi/odroidm1.h
index 43ef5e4..3bb2c51 100644
--- a/wiringPi/odroidm1.h
+++ b/wiringPi/odroidm1.h
@@ -115,6 +115,7 @@
 #endif
 
 extern void init_odroidm1 (struct libodroid *libwiring);
+extern void init_odroidm1s (struct libodroid *libwiring);
 
 #ifdef __cplusplus
 }
diff --git a/wiringPi/odroidm1s.c b/wiringPi/odroidm1s.c
new file mode 100644
index 0000000..ecd1d81
--- /dev/null
+++ b/wiringPi/odroidm1s.c
@@ -0,0 +1,1602 @@
+/*----------------------------------------------------------------------------*/
+//
+//
+//	WiringPi ODROID-M1S Board Control file (ROCKCHIP 64Bits Platform)
+//
+//
+/*----------------------------------------------------------------------------*/
+/*******************************************************************************
+Copyright (C) 2023 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 "odroidm1.h"
+
+#if !defined(DEVMEM)
+#include <gpiod.h>
+/*----------------------------------------------------------------------------*/
+// libgpiod define
+/*----------------------------------------------------------------------------*/
+static struct gpiod_chip *chip;
+static struct gpiod_line *gpiod;
+#endif
+
+/*----------------------------------------------------------------------------*/
+// wiringPi gpio map define
+/*----------------------------------------------------------------------------*/
+static const int pinToGpio[64] = {
+	// wiringPi number to native gpio number
+	16,  71,	//  0 |  1 : GPIO0_C0, GPIO2_A7
+	17,  18,	//  2 |  3 : GPIO0_C1, GPIO0_C2
+	77,  78,	//  4 |  5 : GPIO2_B5, GPIO2_B6
+	72,  14,	//  6 |  7 : GPIO2_B0, GPIO0_B6
+	110,109,	//  8 |  9 : GPIO3_B6, GPIO3_B5
+	97,  73,	// 10 | 11 : GPIO3_A1, GPIO2_B1
+	113,114,	// 12 | 13 : GPIO3_C1, GPIO3_C2
+	115, 68,	// 14 | 15 : GPIO3_C3, GPIO2_A4
+	67,  -1,	// 16 | 17 : GPIO2_A3
+	-1,  -1,	// 18 | 19 :
+	-1,  80,	// 20 | 21 : , GPIO2_C0
+	79,  13,	// 22 | 23 : GPIO2_B7, GPIO0_B5
+	69,  -1,	// 24 | 25 : GPIO2_A5,
+	74,  70,	// 26 | 27 : GPIO2_B2, GPIO2_A6
+	-1,  -1,	// 28 | 29 :
+	 12, 11,	// 30 | 31 : GPIO0_B4, GPIO0_B3
+
+	// 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 const int phyToGpio[64] = {
+	// physical header pin number to native gpio number
+	-1,		//  0
+	-1,  -1,	//  1 |  2 : 3.3V, 5.0V
+	110, -1,	//  3 |  4 : GPIO3_B6, 5.0V
+	109, -1,	//  5 |  6 : GPIO3_B5, GND
+	14,  68,	//  7 |  8 : GPIO0_B6, GPIO2_A4
+	-1,  67,	//  9 | 10 : GND, GPIO2_A3
+	16,  71,	// 11 | 12 : GPIO0_C0, GPIO2_A7
+	17,  -1,	// 13 | 14 : GPIO0_C1, GND
+	18 , 77,	// 15 | 16 : GPIO0_C2, GPIO2_B5
+	-1,  78,	// 17 | 18 : 3.3V, GPIO2_B6
+	113, -1,	// 19 | 20 : GPIO3_C1, GND
+	114, 72,	// 21 | 22 : GPIO3_C2, GPIO2_B0
+	115, 97,	// 23 | 24 : GPIO3_C3, GPIO3_A1
+	-1,  73,	// 25 | 26 : GND, GPIO2_B1
+	12,  11,	// 27 | 28 : GPIO0_B4, GPIO0_B3
+	80,  -1,	// 29 | 30 : GPIO2_C0, GND
+	79,  74,	// 31 | 32 : GPIO2_B7, GPIO2_B2
+	13,  -1,	// 33 | 34 : GPIO0_B5, GND
+	69,  70,	// 35 | 36 : GPIO2_A5, GPIO2_A6
+	-1,  -1,	// 37 | 38 : ADC.AIN1, 1.8V REF
+	-1,  -1,	// 39 | 40 : GND, ADC.AIN0
+
+	// Not used
+	-1, -1, -1, -1, -1, -1, -1, -1,	// 41...48
+	-1, -1, -1, -1, -1, -1, -1, -1,	// 49...56
+	-1, -1, -1, -1, -1, -1, -1	// 57...63
+};
+
+static const char *pinToPwm[64] = {
+	// wiringPi number to pwm group number
+		"None", "None",		   //  0 |  1 : GPIO0_C0, GPIO2_A7
+		"None", "fdd70030",	   //  2 |  3 : GPIO0_C1, GPIO0_C2(PWM3)
+		"None", "None",		   //  4 |  5 : GPIO2_B5, GPIO2_B6
+		"None", "fdd70020",	   //  6 |  7 : GPIO2_B0, GPIO0_B6(PWM2)
+		"None", "None",        //  8 |  9 : GPIO3_B6, GPIO3_B5
+		"None", "None",        // 10 | 11 : GPIO3_A1, GPIO2_B1
+		"None", "None",        // 12 | 13 : GPIO3_C1, GPIO3_C2
+		"None", "None",        // 14 | 15 : GPIO3_C3, GPIO2_A4
+		"None", "None",		   // 16 | 17 : GPIO2_A3
+		"None", "None",		   // 18 | 19 :
+		"None", "None",		   // 20 | 21 : , GPIO2_C0
+		"None", "fdd70010",	   // 22 | 23 : GPIO2_B7, GPIO0_B5(PWM1)
+		"None", "None",		   // 24 | 25 : GPIO2_A5,
+		"None", "None",        // 26 | 27 : GPIO2_B2, GPIO2_A6
+		"None", "None",        // 28 | 29 :
+		"None", "None",        // 30 | 31 : GPIO0_B4, GPIO0_B3
+	// 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, -1,	//  0 |  1 : GPIO0_C0, GPIO2_A7
+	 -1,  2,	//  2 |  3 : GPIO0_C1, GPIO0_C2(PWM3)
+	 -1, -1,	//  4 |  5 : GPIO2_B5, GPIO2_B6
+	 -1,  1,	//  6 |  7 : GPIO2_B0, GPIO0_B6(PWM2)
+	 -1, -1,	//  8 |  9 : GPIO3_B6, GPIO3_B5
+	 -1, -1,	// 10 | 11 : GPIO3_A1, GPIO2_B1
+	 -1, -1,	// 12 | 13 : GPIO3_C1, GPIO3_C2
+	 -1, -1,	// 14 | 15 : GPIO3_C3, GPIO2_A4
+	 -1, -1,	// 16 | 17 : GPIO2_A3
+	 -1, -1,	// 18 | 19 :
+	 -1, -1,	// 20 | 21 : , GPIO2_C0
+	 -1,  0,	// 22 | 23 : GPIO2_B7, GPIO0_B5(PWM1)
+	 -1, -1,	// 24 | 25 : GPIO2_A5,
+	 -1, -1,	// 26 | 27 : GPIO2_B2, GPIO2_A6
+	 -1, -1,	// 28 | 29 :
+	 -1, -1,	// 30 | 31 : GPIO0_B4, GPIO0_B3
+	// 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","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 pin, 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);
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+// libgpiod core function
+/*----------------------------------------------------------------------------*/
+static int		_pinMode_gpiod		(int pin, int mode);
+static int		_digitalRead_gpiod		(int pin);
+static int		_digitalWrite_gpiod		(int pin, int value);
+static int		_digitalWriteByte_gpiod	(const unsigned int value);
+static unsigned int	_digitalReadByte_gpiod	(void);
+#endif
+
+/*----------------------------------------------------------------------------*/
+// board init function
+/*----------------------------------------------------------------------------*/
+static 	void init_gpio_mmap	(void);
+static 	void init_adc_fds	(void);
+void init_odroidm1 	(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 = (M1_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
+//
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static void setClkState (int pin, int state)
+{
+	uint32_t target = 0;
+	uint8_t bank = pin / GPIO_SIZE;
+	uint8_t gpioPclkShift = (bank == 0 ? M1_PMU_CRU_GPIO_PCLK_BIT : (bank * M1_CRU_GPIO_PCLK_BIT));
+
+	target |= (1 << (gpioPclkShift + 16));
+
+	switch (state) {
+	case M1_CLK_ENABLE:
+		if (bank == 0) {
+			target |= *(cru[0] + (M1_PMU_CRU_GPIO_CLK_OFFSET >> 2));
+			target &= ~(1 << gpioPclkShift);
+			*(cru[0] + (M1_PMU_CRU_GPIO_CLK_OFFSET >> 2)) = target;
+		} else {
+			target |= *(cru[1] + (M1_CRU_GPIO_CLK_OFFSET >> 2));
+			target &= ~(1 << gpioPclkShift);
+			*(cru[1] + (M1_CRU_GPIO_CLK_OFFSET >> 2)) = target;
+		}
+		break;
+	case M1_CLK_DISABLE:
+		if (bank == 0) {
+			target |= *(cru[0] + (M1_PMU_CRU_GPIO_CLK_OFFSET >> 2));
+			target |= (1 << gpioPclkShift);
+			*(cru[0] + (M1_PMU_CRU_GPIO_CLK_OFFSET >> 2)) = target;
+		} else {
+			target |= *(cru[1] + (M1_CRU_GPIO_CLK_OFFSET >> 2));
+			target |=  (1 << gpioPclkShift);
+			*(cru[1] + (M1_CRU_GPIO_CLK_OFFSET >> 2)) = target;
+		}
+		break;
+	case M1_CLK_BYTE_ENABLE:
+		setClkState(GPIO_SIZE * 0, M1_CLK_ENABLE);
+		setClkState(GPIO_SIZE * 3, M1_CLK_ENABLE);
+		break;
+	case M1_CLK_BYTE_DISABLE:
+		setClkState(GPIO_SIZE * 0, M1_CLK_DISABLE);
+		setClkState(GPIO_SIZE * 3, M1_CLK_DISABLE);
+		break;
+	default:
+		break;
+	}
+}
+/*----------------------------------------------------------------------------*/
+//
+// set IOMUX mode
+//
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int setIomuxMode (int pin, int mode)
+{
+	uint32_t offset, target;
+	uint8_t	bank, group, bitNum, bitInByte;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+	group = bitNum / 8; // A or B or C or D
+	bitInByte = (pin - (bank * GPIO_SIZE)) % 8;
+
+	offset = (bank == 0 ? 0 : bank-1) * 0x20 + group * 0x8;
+	offset += (bitInByte / 4 == 0) ? 0x0 : 0x4;
+
+	// Common IOMUX Funtion 1 : GPIO (3'h0)
+	switch (mode) {
+	case M1_FUNC_GPIO: // Common IOMUX Function 1_GPIO (3'h0)
+		if (bank == 0) {
+			offset += M1_PMU_GRF_IOMUX_OFFSET;
+			target = *(grf[0] + (offset >> 2));
+			target |= (0x7 << ((bitInByte % 4) * 4 + 16));
+			target &= ~(0x7 << ((bitInByte % 4) * 4)); // ~0x07 = 3'h0
+			*(grf[0] + (offset >> 2)) = target;
+		}
+		else {
+			offset += M1_SYS_GRF_IOMUX_OFFSET;
+			target = *(grf[1] + (offset >> 2));
+			target |= (0x7 << ((bitInByte % 4) * 4 + 16));
+			target &= ~(0x7 << ((bitInByte % 4) * 4));
+			*(grf[1] + (offset >> 2)) = target;
+		}
+		break;
+	case M1_FUNC_PWM:
+		if (bank == 0) {
+			offset += M1_PMU_GRF_IOMUX_OFFSET;
+			target = *(grf[0] + (offset >> 2));
+			target |= (0x7 << ((bitInByte % 4) * 4 + 16));
+			target |= (0x4 << ((bitInByte % 4) * 4)); // gpio0 b5/b6: 3'h100
+			*(grf[0] + (offset >> 2)) = target;
+		}
+		else {
+			offset += M1_SYS_GRF_IOMUX_OFFSET;
+			target = *(grf[1] + (offset >> 2));
+			target |= (0x7 << ((bitInByte % 4) * 4 + 16));
+			target |= (0x5 << ((bitInByte % 4) * 4)); // gpio3 b2: 3'h101
+			*(grf[1] + (offset >> 2)) = target;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _pinMode (int pin, int mode)
+{
+	uint32_t offset, target;
+	uint8_t bank, bitNum;
+	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;
+	bitNum = pin - (bank * GPIO_SIZE);
+	offset = bitNum / 16 == 0 ? M1_GPIO_DIR_OFFSET : M1_GPIO_DIR_OFFSET + 0x4;
+
+	pwmRelease(origPin);
+	softPwmStop(origPin);
+	softToneStop(origPin);
+
+	target = *(gpio[bank] + (offset >> 2));
+	target |= (1 << (gpioToShiftRegBy16(pin) + 16));
+
+	switch (mode) {
+	case INPUT:
+		setIomuxMode(origPin, M1_FUNC_GPIO);
+		target &= ~(1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) = target;
+		_pullUpDnControl(origPin, PUD_OFF);
+		break;
+	case OUTPUT:
+		setIomuxMode(origPin, M1_FUNC_GPIO);
+		target |= (1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) = target;
+		break;
+	case INPUT_PULLUP:
+		setIomuxMode(origPin, M1_FUNC_GPIO);
+		target &= ~(1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) = target;
+		_pullUpDnControl(origPin, PUD_UP);
+		break;
+	case INPUT_PULLDOWN:
+		setIomuxMode(origPin, M1_FUNC_GPIO);
+		target &= ~(1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) = target;
+		_pullUpDnControl(origPin, PUD_DOWN);
+		break;
+	case SOFT_PWM_OUTPUT:
+		softPwmCreate(origPin, 0, 100);
+		break;
+	case SOFT_TONE_OUTPUT:
+		softToneCreate(origPin);
+		break;
+	case	PWM_OUTPUT:
+		pwmSetup(origPin);
+		break;
+	default:
+		msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+		return -1;
+	}
+
+	return 0;
+}
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _pinMode_gpiod (int pin, int mode)
+{
+	uint8_t bank, bitNum;
+	int origPin, ret;
+
+	origPin = pin;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+
+	pwmRelease(origPin);
+	softPwmStop(origPin);
+	softToneStop(origPin);
+
+	chip = gpiod_chip_open_by_number(bank);
+	if (!chip) {
+		return -1;
+	}
+
+	gpiod = gpiod_chip_get_line(chip, bitNum);
+	if (!gpiod) {
+		printf("gpio get error\n");
+		gpiod_chip_close(chip);
+		return -1;
+	}
+
+	switch (mode) {
+	case INPUT:
+		ret = gpiod_line_request_input(gpiod, CONSUMER);
+		if (ret < 0) {
+			printf("gpiod request error\n");
+			gpiod_line_release(gpiod);
+		}
+		_pullUpDnControl(origPin, PUD_OFF);
+		break;
+	case OUTPUT:
+		ret = gpiod_line_request_output(gpiod, CONSUMER, 0);
+		if (ret < 0) {
+			printf("gpiod request error\n");
+			gpiod_line_release(gpiod);
+		}
+		break;
+	case INPUT_PULLUP:
+		ret = gpiod_line_request_input(gpiod, CONSUMER);
+		if (ret < 0) {
+			printf("gpiod request error\n");
+			gpiod_line_release(gpiod);
+		}
+		_pullUpDnControl(origPin, PUD_UP);
+		break;
+	case INPUT_PULLDOWN:
+		ret = gpiod_line_request_input(gpiod, CONSUMER);
+		if (ret < 0) {
+			printf("gpiod request error\n");
+			gpiod_line_release(gpiod);
+		}
+		_pullUpDnControl(origPin, PUD_DOWN);
+		break;
+	case SOFT_PWM_OUTPUT:
+		softPwmCreate(origPin, 0, 100);
+		break;
+	case SOFT_TONE_OUTPUT:
+		softToneCreate(origPin);
+		break;
+	default:
+		msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+		return -1;
+	}
+
+	gpiod_line_release(gpiod);
+	gpiod_chip_close(chip);
+
+	return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _getDrive(int pin)
+{
+	uint32_t offset, target;
+	uint8_t bank, group, bitNum, bitInByte;
+	int value = 0;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+	bitInByte = bitNum % 8;
+	group = bitNum / 8;
+
+	if (bank == 0) {
+		offset = M1_PMU_GRF_DS_OFFSET;
+		offset += (group * 0x10);
+		offset += ((bitInByte / 2) * 0x4);
+		target = *(grf[0] + (offset >> 2));
+	}
+	else {
+		offset = M1_SYS_GRF_DS_OFFSET;
+		offset += ((bank - 1) * 0x40);
+		offset += (group * 0x10);
+		offset += ((bitInByte / 2) * 0x4);
+		target = *(grf[1] + (offset >> 2));
+	}
+
+	target &= 0x3f3f; //reset reserved bits
+	target = (bitInByte % 2 == 0 ? target & 0x3f : target >> 8);
+
+	switch (target) {
+		case DS_LEVEL_0:
+			value = 0;
+			break;
+		case DS_LEVEL_1:
+			value = 1;
+			break;
+		case DS_LEVEL_2:
+			value = 2;
+			break;
+		case DS_LEVEL_3:
+			value = 3;
+			break;
+		case DS_LEVEL_4:
+			value = 4;
+			break;
+		case DS_LEVEL_5:
+			value = 5;
+			break;
+		default:
+			value = -1;
+			break;
+	}
+
+	return value;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _setDrive(int pin, int value)
+{
+	uint32_t offset, target;
+	uint8_t bank, group, bitNum, bitInByte;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+	bitInByte = bitNum % 8;
+	group = bitNum / 8;
+
+	if (bank == 0) {
+		offset = M1_PMU_GRF_DS_OFFSET;
+		offset += (group * 0x10);
+		offset += ((bitInByte / 2) * 0x4);
+		target = *(grf[0] + (offset >> 2));
+	}
+	else {
+		offset = M1_SYS_GRF_DS_OFFSET;
+		offset += ((bank - 1) * 0x40);
+		offset += (group * 0x10);
+		offset += ((bitInByte / 2) * 0x4);
+		target = *(grf[1] + (offset >> 2));
+	}
+
+	target |= (0x3f3f << 16);
+	target &= ~(bitInByte % 2 == 0 ? 0x3f << 0 : 0x3f << 8);
+
+	switch (value) {
+		case 0:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_0 : (DS_LEVEL_0 << 8));
+			break;
+		case 1:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_1 : (DS_LEVEL_1 << 8));
+			break;
+		case 2:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_2 : (DS_LEVEL_2 << 8));
+			break;
+		case 3:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_3 : (DS_LEVEL_3 << 8));
+			break;
+		case 4:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_4 : (DS_LEVEL_4 << 8));
+			break;
+		case 5:
+			target |= (bitInByte % 2 == 0 ? DS_LEVEL_5 : (DS_LEVEL_5 << 8));
+			break;
+		default:
+			break;
+	}
+
+	if (bank == 0) {
+		*(grf[0] + (offset >> 2)) = target;
+	}
+	else {
+		*(grf[1] + (offset >> 2)) = target;
+	}
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _getAlt (int pin)
+{
+	// TODO: Working confirmed
+	uint32_t offset;
+	uint16_t ret = 0;
+	uint8_t	bank, group, bitNum, bitInByte, shift;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	bank = pin / GPIO_SIZE; // GPIO0, GPIO1, ...
+	bitNum = pin - (bank * GPIO_SIZE);
+	bitInByte = bitNum % 8;
+	group = bitNum / 8; // GPIO0_A, GPIO0_B, ...
+
+	// Move to the proper IOMUX register regardless of whether it is L, H.
+	offset = 0x20 * (bank == 0 ? 0x0 : bank - 1) + 0x8 * group;
+
+	// Check where the register this pin located in
+	offset += (bitInByte / 4 == 0) ? 0x0 : 0x4;
+
+	// The shift to move to the target pin at the register
+	shift = bitInByte % 4 * 4;
+
+	// Check if the pin is GPIO mode on GRF register
+	if (bank == 0) {
+		offset += M1_PMU_GRF_IOMUX_OFFSET; //0x00
+		ret = (*(grf[0] + (offset >> 2)) >> shift) & 0x7;
+	} else {
+		offset += M1_SYS_GRF_IOMUX_OFFSET; //0x00
+		ret = (*(grf[1] + (offset >> 2)) >> shift) & 0x7;
+	}
+
+	// If it is ALT0 (GPIO mode), check it's direction
+	// Add offset 0x4 to go to H register
+	// when the bit group is in the high two-bytes of the word size
+	if (ret == 0) {
+		if (bitNum / 16 == 0)
+			offset = (M1_GPIO_DIR_OFFSET >> 2);
+		else
+			offset = ((M1_GPIO_DIR_OFFSET + 0x4) >> 2);
+		ret = !!(*(gpio[bank] + offset) & (1 << gpioToShiftRegBy16(bitNum)));
+	}
+	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;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _getPUPD (int pin)
+{
+	uint32_t offset, pupd;
+	uint8_t bank, group, bitNum, bitInByte;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode,pin)) < 0)
+		return -1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+	group = bitNum / 8;
+	bitInByte = bitNum % 8;
+	pupd = 0x00;
+	pupd = (0x3 << (bitInByte * 2));
+	offset = (bank == 0) ? M1_PMU_GRF_PUPD_OFFSET + (group * 0x4) :  M1_SYS_GRF_PUPD_OFFSET + (group * 0x4) + ((bank - 1) * 0x10);
+
+	if (bank == 0)
+		pupd &= *(grf[0] + (offset >> 2));
+	else
+		pupd &= *(grf[1] + (offset >> 2));
+
+	pupd = (pupd >> bitInByte * 2);
+
+	return pupd;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _pullUpDnControl (int pin, int pud)
+{
+	uint32_t offset, target;
+	uint8_t	bank, group, bitNum, bitInByte;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0) //exit
+		return	-1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+	group = bitNum / 8;
+	bitInByte = bitNum % 8;
+	offset = (bank == 0) ? M1_PMU_GRF_PUPD_OFFSET + (group * 0x4) :  M1_SYS_GRF_PUPD_OFFSET + (group * 0x4) + ((bank - 1) * 0x10);
+
+	switch (pud) {
+	case PUD_UP:
+		if (bank == 0)
+		{
+			target = *(grf[0] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			target |= (0x1 << (bitInByte * 2));
+			*(grf[0] + (offset >> 2)) = target;
+		}
+		else
+		{
+			target = *(grf[1] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			target |= (0x1 << (bitInByte * 2));
+			*(grf[1] + (offset >> 2)) = target;
+		}
+		break;
+	case PUD_DOWN:
+		if (bank == 0)
+		{
+			target = *(grf[0] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			target |= (0x2 << (bitInByte * 2));
+			*(grf[0] + (offset >> 2)) = target;
+		}
+		else
+		{
+			target = *(grf[1] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			target |= (0x2 << (bitInByte * 2));
+			*(grf[1] + (offset >> 2)) = target;
+		}
+		break;
+	case PUD_OFF:
+		if (bank == 0)
+		{
+			target = *(grf[0] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			*(grf[0] + (offset >> 2)) = target;
+		}
+		else
+		{
+			target = *(grf[1] + (offset >> 2));
+			target |= (0x3 << ((bitInByte * 2) + 16));
+			target &= ~(0x3 << (bitInByte * 2));
+			*(grf[1] + (offset >> 2)) = target;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))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] + (M1_GPIO_GET_OFFSET >> 2)) & (1 << gpioToShiftRegBy32(pin)) ? HIGH : LOW;
+
+	return ret;
+}
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _digitalRead_gpiod (int pin)
+{
+	uint8_t bank, bitNum;
+	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;
+	bitNum = pin - (bank * GPIO_SIZE);
+
+	chip = gpiod_chip_open_by_number(bank);
+	if (!chip) {
+		printf("chip open error\n");
+		return -1;
+	}
+	gpiod = gpiod_chip_get_line(chip,bitNum);
+	if (!gpiod) {
+		printf("gpiod get error\n");
+		gpiod_chip_close(chip);
+	}
+	ret = gpiod_line_get_value(gpiod);
+	if (ret < 0) {
+		printf("gpiod get error\n");
+		gpiod_line_release(gpiod);
+	}
+
+	gpiod_line_release(gpiod);
+	gpiod_chip_close(chip);
+
+	return ret;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _digitalWrite (int pin, int value)
+{
+	uint32_t offset, target;
+	uint8_t bank, bitNum;
+
+	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 + M1_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 + M1_GPIO_PIN_BASE);
+			}
+		}
+		return -1;
+	}
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank*GPIO_SIZE);
+
+	offset = (bitNum / 16 == 0 ? M1_GPIO_SET_OFFSET : M1_GPIO_SET_OFFSET + 0x04);
+
+	target = *(gpio[bank] + (offset >> 2));
+	target |= (1 << (gpioToShiftRegBy16(pin)+16));
+
+	switch (value) {
+	case LOW:
+		target &= ~(1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) = target;
+		break;
+	case HIGH:
+		target |= (1 << gpioToShiftRegBy16(pin));
+		*(gpio[bank] + (offset >> 2)) =  target;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _digitalWrite_gpiod (int pin, int value)
+{
+	uint8_t bank,bitNum;
+	int ret;
+
+	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 + M1_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 + M1_GPIO_PIN_BASE);
+			}
+		}
+		return -1;
+	}
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	bank = pin / GPIO_SIZE;
+	bitNum = pin - (bank * GPIO_SIZE);
+
+	chip = gpiod_chip_open_by_number(bank);
+	if (!chip) {
+		printf("chip open error\n");
+		return -1;
+	}
+
+	gpiod = gpiod_chip_get_line(chip, bitNum);
+	if (!gpiod) {
+		printf("gpiod get error\n");
+		gpiod_chip_close(chip);
+	}
+
+	switch (value) {
+	case LOW:
+		ret = gpiod_line_set_value(gpiod, 0);
+		if (ret < 0) {
+			printf("gpiod set error\n");
+			gpiod_line_release(gpiod);
+		}
+		break;
+	case HIGH:
+		ret = gpiod_line_set_value(gpiod, 1);
+		if (ret < 0) {
+			printf("gpiod set error\n");
+			gpiod_line_release(gpiod);
+		}
+		break;
+	default:
+		break;
+	}
+
+	gpiod_line_release(gpiod);
+	gpiod_chip_close(chip);
+
+	return 0;
+}
+#endif
+
+__attribute__ ((unused))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);
+}
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _digitalWriteByte (const unsigned int value)
+{
+	union reg_bitfield gpio0;
+	union reg_bitfield gpio3;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return -1;
+	}
+
+	/* Read data register */
+	gpio0.wvalue = *(gpio[0] + (M1_GPIO_GET_OFFSET >> 2));
+	gpio3.wvalue = *(gpio[3] + (M1_GPIO_GET_OFFSET >> 2));
+
+	/* Wiring PI GPIO0 = M1 GPIO0_C.0 */
+	gpio0.bits.bit16 = ((value & 0x01) >> 0);
+	/* Wiring PI GPIO1 = M1 GPIO3_D.0 */
+	gpio3.bits.bit24 = ((value & 0x02) >> 1);
+	/* Wiring PI GPIO2 = M1 GPIO0_C.1 */
+	gpio0.bits.bit17 = ((value & 0x04) >> 2);
+	/* Wiring PI GPIO3 = M1 GPIO3_B.2 */
+	gpio3.bits.bit10 = ((value & 0x08) >> 3);
+	/* Wiring PI GPIO4 = M1 GPIO3_C.6 */
+	gpio3.bits.bit22 = ((value & 0x10) >> 4);
+	/* Wiring PI GPIO5 = M1 GPIO3_C.7 */
+	gpio3.bits.bit23 = ((value & 0x20) >> 5);
+	/* Wiring PI GPIO6 = M1 GPIO3_D.1 */
+	gpio3.bits.bit25 = ((value & 0x40) >> 6);
+	/* Wiring PI GPIO7 = M1 GPIO0_B.6 */
+	gpio0.bits.bit14 = ((value & 0x80) >> 7);
+
+	/* Update data register */
+	*(gpio[0] + ((M1_GPIO_SET_OFFSET + 0x4) >> 2)) = (WRITE_BYTE_MASK_GPIO0_H | (gpio0.wvalue >> 16));
+	*(gpio[0] + (M1_GPIO_SET_OFFSET >> 2)) = (WRITE_BYTE_MASK_GPIO0_L | (gpio0.wvalue & 0xffff));
+
+	*(gpio[3] + ((M1_GPIO_SET_OFFSET + 0x4) >> 2)) = (WRITE_BYTE_MASK_GPIO3_H | (gpio3.wvalue >> 16));
+	*(gpio[3] + (M1_GPIO_SET_OFFSET >> 2)) = (WRITE_BYTE_MASK_GPIO3_L | (gpio3.wvalue & 0xffff));
+
+	return 0;
+}
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static int _digitalWriteByte_gpiod (const unsigned int value)
+{
+	static struct gpiod_chip *chip[2];
+	static struct gpiod_line *gpiodLines[8];
+
+	uint32_t unit=0x0;
+	int ret;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return -1;
+	}
+
+	chip[0] = gpiod_chip_open_by_number(0);
+	chip[1] = gpiod_chip_open_by_number(3);
+
+	//gpiod get lines
+	/* Wiring PI GPIO0 = M1 GPIO0_C.0 */
+	gpiodLines[0] = gpiod_chip_get_line(chip[0], 16);
+	/* Wiring PI GPIO1 = M1 GPIO3_D.0 */
+	gpiodLines[1] = gpiod_chip_get_line(chip[1], 24);
+	/* Wiring PI GPIO2 = M1 GPIO0_C.1 */
+	gpiodLines[2] = gpiod_chip_get_line(chip[0], 17);
+	/* Wiring PI GPIO3 = M1 GPIO3_B.2 */
+	gpiodLines[3] = gpiod_chip_get_line(chip[1], 10);
+	/* Wiring PI GPIO4 = M1 GPIO3_C.6 */
+	gpiodLines[4] = gpiod_chip_get_line(chip[1], 22);
+	/* Wiring PI GPIO5 = M1 GPIO3_C.7 */
+	gpiodLines[5] = gpiod_chip_get_line(chip[1], 23);
+	/* Wiring PI GPIO6 = M1 GPIO3_D.1 */
+	gpiodLines[6] = gpiod_chip_get_line(chip[1], 25);
+	/* Wiring PI GPIO7 = M1 GPIO0_B.6 */
+	gpiodLines[7] = gpiod_chip_get_line(chip[0], 14);
+	for (int i = 0; i < 8; i++) {
+		if (!gpiodLines[i]) {
+			printf("gpiod get error pin:%d\n",i);
+			for (int j = 0; j < 2; j++)
+				gpiod_chip_close(chip[j]);
+		}
+	}
+
+	for (int i = 0; i < 8; i++) {
+		unit = (1 << i);
+		ret = gpiod_line_request_output(gpiodLines[i],CONSUMER,0);
+		if (ret < 0) {
+			printf("gpiod request error pin:%d\n" , i);
+			gpiod_line_release(gpiodLines[i]);
+		}
+		ret = gpiod_line_set_value(gpiodLines[i],((value & unit) >> i));
+		if (ret < 0) {
+			printf("gpiod set error pin:%d\n" , i);
+			gpiod_line_release(gpiodLines[i]);
+		}
+	}
+
+	for (int i = 0; i < 8; i++)
+		gpiod_line_release(gpiodLines[i]);
+	for (int i = 0; i < 2; i++)
+		gpiod_chip_close(chip[i]);
+
+	return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static unsigned int _digitalReadByte (void)
+{
+	union reg_bitfield gpio0;
+	union reg_bitfield gpio3;
+
+	unsigned int value = 0;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return	-1;
+	}
+
+	/* Read data register */
+	gpio0.wvalue = *(gpio[0] + (M1_GPIO_GET_OFFSET >> 2));
+	gpio3.wvalue = *(gpio[3] + (M1_GPIO_GET_OFFSET >> 2));
+
+	/* Wiring PI GPIO0 = M1 GPIO0_C.0 */
+	if (gpio0.bits.bit16)
+		value |= 0x01;
+	/* Wiring PI GPIO1 = M1 GPIO3_D.0 */
+	if (gpio3.bits.bit24)
+		value |= 0x02;
+	/* Wiring PI GPIO2 = M1 GPIO0_C.1 */
+	if (gpio0.bits.bit17)
+		value |= 0x04;
+	/* Wiring PI GPIO3 = M1 GPIO3_B.2 */
+	if (gpio3.bits.bit10)
+		value |= 0x08;
+	/* Wiring PI GPIO4 = M1 GPIO3_C.6 */
+	if (gpio3.bits.bit22)
+		value |= 0x10;
+	/* Wiring PI GPIO5 = M1 GPIO3_C.7 */
+	if (gpio3.bits.bit23)
+		value |= 0x20;
+	/* Wiring PI GPIO6 = M1 GPIO3_D.1 */
+	if (gpio3.bits.bit25)
+		value |= 0x40;
+	/* Wiring PI GPIO7 = M1 GPIO0_B.6 */
+	if (gpio0.bits.bit14)
+		value |= 0x80;
+
+	return value;
+}
+
+#if !defined(DEVMEM)
+/*----------------------------------------------------------------------------*/
+__attribute__ ((unused))static unsigned int _digitalReadByte_gpiod (void)
+{
+	static struct gpiod_chip *chip[2];
+	static struct gpiod_line *gpiodLines[8];
+
+	unsigned int value = 0;
+	uint32_t unit = 0x0;
+	int ret;
+
+	if (lib->mode == MODE_GPIO_SYS) {
+		return -1;
+	}
+
+	chip[0] = gpiod_chip_open_by_number(0);
+	chip[1] = gpiod_chip_open_by_number(3);
+
+	//gpiod get lines
+	/* Wiring PI GPIO0 = M1 GPIO0_C.0 */
+	gpiodLines[0] = gpiod_chip_get_line(chip[0], 16);
+	/* Wiring PI GPIO1 = M1 GPIO3_D.0 */
+	gpiodLines[1] = gpiod_chip_get_line(chip[1], 24);
+	/* Wiring PI GPIO2 = M1 GPIO0_C.1 */
+	gpiodLines[2] = gpiod_chip_get_line(chip[0], 17);
+	/* Wiring PI GPIO3 = M1 GPIO3_B.2 */
+	gpiodLines[3] = gpiod_chip_get_line(chip[1], 10);
+	/* Wiring PI GPIO4 = M1 GPIO3_C.6 */
+	gpiodLines[4] = gpiod_chip_get_line(chip[1], 22);
+	/* Wiring PI GPIO5 = M1 GPIO3_C.7 */
+	gpiodLines[5] = gpiod_chip_get_line(chip[1], 23);
+	/* Wiring PI GPIO6 = M1 GPIO3_D.1 */
+	gpiodLines[6] = gpiod_chip_get_line(chip[1], 25);
+	/* Wiring PI GPIO7 = M1 GPIO0_B.6 */
+	gpiodLines[7] = gpiod_chip_get_line(chip[0], 14);
+	for (int i = 0; i < 8; i++) {
+		if (!gpiodLines[i]) {
+			printf("gpiod get error pin:%d\n",i);
+			for (int j = 0; j < 2; j++)
+				gpiod_chip_close(chip[j]);
+		}
+	}
+
+	for (int i = 0; i < 8; i++) {
+		unit = (1 << i);
+		ret = gpiod_line_request_input(gpiodLines[i],CONSUMER);
+		if (ret < 0) {
+			printf("gpiod request error pin:%d\n",i);
+			gpiod_line_release(gpiodLines[i]);
+		}
+		ret = gpiod_line_get_value(gpiodLines[i]);
+		if (ret)
+			value |= unit;
+	}
+
+	for (int i = 0; i < 8; i++)
+		gpiod_line_release(gpiodLines[i]);
+	for (int i = 0; i < 2; i++)
+		gpiod_chip_close(chip[i]);
+
+	return value;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+// 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",
+				(M1_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, M1_PMU_CRU_BASE);
+		mapped_cru[1] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_CRU_BASE);
+
+		mapped_grf[0] = mmap(0, M1_GRF_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_PMU_GRF_BASE);
+		mapped_grf[1] = mmap(0, M1_GRF_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_SYS_GRF_BASE);
+
+		mapped_gpio[0] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_GPIO_0_BASE);
+		mapped_gpio[1] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_GPIO_1_BASE);
+		mapped_gpio[2] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_GPIO_2_BASE);
+		mapped_gpio[4] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_GPIO_4_BASE);
+		mapped_gpio[3] = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, M1_GPIO_3_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/fe720000.saradc/iio:device0/in_voltage3_raw";
+	AIN1_NODE = "/sys/devices/platform/fe720000.saradc/iio:device0/in_voltage2_raw";
+
+	adcFds[0] = open(AIN0_NODE, O_RDONLY);
+	adcFds[1] = open(AIN1_NODE, O_RDONLY);
+}
+/*----------------------------------------------------------------------------*/
+void init_odroidm1s (struct libodroid *libwiring)
+{
+	init_gpio_mmap();
+
+	init_adc_fds();
+#if defined(DEVMEM)
+	/* 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;
+#else
+	/* wiringPi-libgpiod Core function initialize */
+	libwiring->getModeToGpio	= _getModeToGpio;
+	libwiring->pinMode		= _pinMode_gpiod;
+	libwiring->getAlt		= _getAlt;
+	libwiring->getPUPD		= _getPUPD;
+	libwiring->pullUpDnControl	= _pullUpDnControl;
+	libwiring->getDrive			= _getDrive;
+	libwiring->setDrive			= _setDrive;
+	libwiring->digitalRead		= _digitalRead_gpiod;
+	libwiring->digitalWrite		= _digitalWrite_gpiod;
+	libwiring->analogRead		= _analogRead;
+	libwiring->digitalWriteByte	= _digitalWriteByte_gpiod;
+	libwiring->digitalReadByte	= _digitalReadByte_gpiod;
+#endif
+	/* specify pin base number */
+	libwiring->pinBase		= M1_GPIO_PIN_BASE;
+
+	/* global variable setup */
+	lib = libwiring;
+}
+/*----------------------------------------------------------------------------*/
diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c
index 8008541..d7bdbf3 100644
--- a/wiringPi/wiringPi.c
+++ b/wiringPi/wiringPi.c
@@ -64,6 +64,7 @@
 	"ODROID-C4",
 	"ODROID-HC4",
 	"ODROID-M1",
+	"ODROID-M1S",
 };
 
 const char *piRevisionNames [16] =
@@ -538,6 +539,11 @@
 			libwiring.mem = 5;
 			libwiring.rev = 1;
 			break;
+		case MODEL_ODROID_M1S:
+			libwiring.maker = MAKER_ROCKCHIP;
+			libwiring.mem = 5;
+			libwiring.rev = 1;
+			break;
 		case MODEL_UNKNOWN:
 		default:
 			libwiring.model = MAKER_UNKNOWN;
@@ -1260,6 +1266,9 @@
 	case MODEL_ODROID_M1:
 		init_odroidm1(&libwiring);
 	break;
+	case MODEL_ODROID_M1S:
+		init_odroidm1s(&libwiring);
+	break;
 	default:
 		return wiringPiFailure (WPI_ALMOST,
 			"wiringPiSetup: Unknown model\n");
diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h
index 177cfa3..37aba34 100644
--- a/wiringPi/wiringPi.h
+++ b/wiringPi/wiringPi.h
@@ -61,6 +61,7 @@
 #define	MODEL_ODROID_C4		6
 #define	MODEL_ODROID_HC4	7
 #define	MODEL_ODROID_M1		8
+#define	MODEL_ODROID_M1S	9
 
 #define	MAKER_UNKNOWN		0
 #define	MAKER_AMLOGIC		1
diff --git a/wiringPi/wiringPiI2C.c b/wiringPi/wiringPiI2C.c
index 6d77809..14bc5f8 100644
--- a/wiringPi/wiringPiI2C.c
+++ b/wiringPi/wiringPiI2C.c
@@ -267,6 +267,9 @@
 		else
 			device = "/dev/i2c-2";
 	break;
+	case MODEL_ODROID_M1S:
+		device = "/dev/i2c-0";
+	break;
 	}
 
 	return wiringPiI2CSetupInterface (device, devId) ;
diff --git a/wiringPi/wiringPiSPI.c b/wiringPi/wiringPiSPI.c
index 80626fc..9730c50 100644
--- a/wiringPi/wiringPiSPI.c
+++ b/wiringPi/wiringPiSPI.c
@@ -149,6 +149,7 @@
 	case MODEL_ODROID_N2:
 	case MODEL_ODROID_C4:
 	case MODEL_ODROID_M1:
+	case MODEL_ODROID_M1S:
 		sprintf(device, "%s%d", spiDevType0, channel);
 	break;
 	case MODEL_ODROID_XU3: