WiringPi: Add support for Odroid-HC4

Signed-off-by: Deokgyu Yang <secugyu@gmail.com>
Change-Id: If64c712eef681a2a9aa4b7828d95b612a1de91db
diff --git a/gpio/gpio.c b/gpio/gpio.c
index b2f5b0f..b1f60a0 100644
--- a/gpio/gpio.c
+++ b/gpio/gpio.c
@@ -220,6 +220,7 @@
 		break;
 	case MODEL_ODROID_N2:
 	case MODEL_ODROID_C4:
+	case MODEL_ODROID_HC4:
 		if (cmpKernelVersion(KERN_NUM_TO_REVISION, 4, 9, 230))
 			port = 0;
 		else
diff --git a/gpio/readall.c b/gpio/readall.c
index 462a22f..db9c764 100644
--- a/gpio/readall.c
+++ b/gpio/readall.c
@@ -110,6 +110,15 @@
 } ;
 
 /*----------------------------------------------------------------------------*/
+static const int physToWpiHC4 [64] =
+{
+	-1,	// 0
+	-1,  0,	// 1, 2
+	 1,  2,
+	-1,
+} ;
+
+/*----------------------------------------------------------------------------*/
 static const char *physNamesOdroidC1All [64] =
 {
 	NULL,
@@ -544,6 +553,44 @@
 };
 
 /*----------------------------------------------------------------------------*/
+static const char *physNamesOdroidHC4All [64] =
+{
+	NULL,
+
+	"    3.3V",
+	"   SDA.2",
+	"   SCL.2",
+	"GPIO.481",
+	"      0V",
+
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,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 *physNamesOdroidHC4 [64] =
+{
+	NULL,
+
+	"   3.3V",
+	"  SDA.2",
+	"  SCL.2",
+	" IO.481",
+	"     0V",
+
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+	NULL,NULL,NULL,NULL,NULL,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 ;
 
@@ -670,7 +717,6 @@
 	printf (" |\n") ;
 }
 
-/*----------------------------------------------------------------------------*/
 static void printHeader(const char *headerName, int isAll) {
 	const char *headerLeft = " +-----+-----+---------+------+---+";
 	const char *headerRight = "+---+------+---------+-----+-----+\n";
@@ -703,6 +749,94 @@
 }
 
 /*----------------------------------------------------------------------------*/
+static void readallPhysHC4(int model, int UNU rev, int physPin, const char *physNames[], int isAll) {
+	int pin ;
+
+	// GPIO, wPi pin number
+	if (isAll == TRUE) {
+		if ((physPinToGpio (physPin) == -1) && (physToWpiHC4 [physPin] == -1))
+			printf(" |      |    ");
+		else if (physPinToGpio (physPin) != -1) {
+			printf(" |  %3d | %3d", physPinToGpio(physPin), physToWpiHC4[physPin]);
+		} else
+			printf(" |      | %3d", physToWpiHC4 [physPin]);
+	} else {
+		if ((physPinToGpio (physPin) == -1) && (physToWpiHC4 [physPin] == -1))
+			printf(" |     |    ");
+		else if (physPinToGpio (physPin) != -1) {
+			printf(" | %3d | %3d", physPinToGpio(physPin), physToWpiHC4[physPin]);
+		} else
+			printf(" |     | %3d", physToWpiHC4 [physPin]);
+	}
+
+	// GPIO pin name
+	printf (" | %s", physNames [physPin]) ;
+
+	// GPIO pin mode, value
+	if ((physToWpiHC4 [physPin] == -1) || (physPinToGpio (physPin) == -1)) {
+		printf(" |      |  ");
+		if (isAll == TRUE)
+			printf(" |    |      ");
+	} else {
+		if (wpMode == MODE_GPIO)
+			pin = physPinToGpio (physPin);
+		else if (wpMode == MODE_PHYS)
+			pin = physPin ;
+		else
+			pin = physToWpiHC4 [physPin];
+
+		printf (" | %4s", alts [getAlt (pin)]) ;
+		printf (" | %d", digitalRead (pin)) ;
+
+		// GPIO pin drive strength, pu/pd
+		if (isAll == TRUE) {
+			switch (model) {
+			case MODEL_ODROID_HC4:
+				printf (" | %2d | %5s", getDrive(pin), pupd[getPUPD(pin)]);
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	// Physical pin number
+	printf(" |  %2d |\n", physPin);
+}
+
+/*----------------------------------------------------------------------------*/
+static void printHeaderHC4(const char *headerName, int isAll) {
+	const char *header = " +-----+-----+---------+------+---+-----+";
+	const char *headerAll = " +------+-----+----------+------+---+----+-------+-----+";
+
+	(isAll == FALSE) ? printf("%s\n", header) : printf("%s\n", headerAll);
+	(isAll == FALSE)
+		? printf(" |              %s              |\n", headerName)
+		: printf(" |             %s              |\n", headerName);
+	(isAll == FALSE) ? printf("%s\n", header) : printf("%s\n", headerAll);
+}
+
+/*----------------------------------------------------------------------------*/
+static void printBodyHC4(int model, int rev, const char *physNames[], int isAll) {
+	(isAll == FALSE)
+		? printf(
+			" | I/O | wPi |   Name  | Mode | V | Phy |\n"
+			" +-----+-----+---------+------+---+-----+\n")
+		: printf(
+			" | GPIO | wPi |   Name   | Mode | V | DS | PU/PD | Phy |\n"
+			" +------+-----+----------+------+---+----+-------+-----+\n");
+	for (int pin = 1; pin <= 5; pin ++)
+		readallPhysHC4(model, rev, pin, physNames, isAll);
+	(isAll == FALSE)
+		? printf(
+			" +-----+-----+---------+------+---+-----+\n"
+			" | I/O | wPi |   Name  | Mode | V | Phy |\n")
+		: printf(
+			" +------+-----+----------+------+---+----+-------+-----+\n"
+			" | GPIO | wPi |   Name   | Mode | V | DS | PU/PD | Phy |\n");
+}
+
+/*----------------------------------------------------------------------------*/
 /*
  * doReadall:
  *	Read all the GPIO pins
@@ -764,14 +898,27 @@
 			headerName = (isAll == FALSE) ? "--- C4 ---" : "---- Model  ODROID-C4 ----";
 			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidC4 : physNamesOdroidC4All);
 			break;
+		case MODEL_ODROID_HC4:
+			headerName = (isAll == FALSE) ? "   HC4    " : "     Model ODROID-HC4     ";
+			physNames = (char *) ((isAll == FALSE) ? physNamesOdroidHC4 : physNamesOdroidHC4All);
+			break;
 		default:
 			printf("Oops - unknown model: %d\n", model);
 			return;
 	}
 
-	printHeader((const char *) headerName, isAll);
-	printBody(model, rev, (const char **) physNames, isAll);
-	printHeader((const char *) headerName, isAll);
+	switch (model) {
+		case MODEL_ODROID_HC4:
+			printHeaderHC4((const char *) headerName, isAll);
+			printBodyHC4(model, rev, (const char **) physNames, isAll);
+			printHeaderHC4((const char *) headerName, isAll);
+			break;
+		default:
+			printHeader((const char *) headerName, isAll);
+			printBody(model, rev, (const char **) physNames, isAll);
+			printHeader((const char *) headerName, isAll);
+			break;
+	}
 }
 
 /*----------------------------------------------------------------------------*/
diff --git a/wiringPi/Makefile b/wiringPi/Makefile
index 2598a4c..3031a30 100644
--- a/wiringPi/Makefile
+++ b/wiringPi/Makefile
@@ -68,7 +68,8 @@
 		odroidxu3.c						\
 		odroidn1.c						\
 		odroidn2.c						\
-		odroidc4.c
+		odroidc4.c						\
+		odroidhc4.c
 
 HEADERS =	$(shell ls *.h)
 
@@ -182,3 +183,4 @@
 odroidn1.o  : wiringPi.h odroidn1.h
 odroidn2.o  : wiringPi.h odroidn2.h
 odroidc4.o  : wiringPi.h odroidc4.h
+odroidhc4.o  : wiringPi.h odroidc4.h
diff --git a/wiringPi/odroidc4.h b/wiringPi/odroidc4.h
index bb43962..77ee6c5 100644
--- a/wiringPi/odroidc4.h
+++ b/wiringPi/odroidc4.h
@@ -54,6 +54,7 @@
 #endif
 
 extern void init_odroidc4 (struct libodroid *libwiring);
+extern void init_odroidhc4 (struct libodroid *libwiring);
 
 #ifdef __cplusplus
 }
diff --git a/wiringPi/odroidhc4.c b/wiringPi/odroidhc4.c
new file mode 100644
index 0000000..ea714c7
--- /dev/null
+++ b/wiringPi/odroidhc4.c
@@ -0,0 +1,577 @@
+/*----------------------------------------------------------------------------*/
+//
+//
+//	WiringPi ODROID-C4 Board Control file (AMLogic 64Bits Platform)
+//
+//
+/*----------------------------------------------------------------------------*/
+#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 "softPwm.h"
+#include "softTone.h"
+
+/*----------------------------------------------------------------------------*/
+#include "wiringPi.h"
+#include "odroidc4.h"
+
+/*----------------------------------------------------------------------------*/
+// wiringPi gpio map define
+/*----------------------------------------------------------------------------*/
+static const int pinToGpio[64] = {
+	// wiringPi number to native gpio number
+	493, 494,	//  0 |  1 : GPIOX.17(I2C-2_SDA), GPIOX.18(I2C-2_SCL)
+	481,  -1,	//  2 |  3 : , GPIOX.5
+	 -1,  -1,	//  4 |  5 :
+	 -1,  -1,	//  6 |  7 :
+	 -1,  -1,	//  8 |  9 :
+	 -1,  -1,	// 10 | 11 :
+	 -1,  -1,	// 12 | 13 :
+	 -1,  -1,	// 14 | 15 :
+	 -1,  -1,	// 16 | 17 :
+	 -1,  -1,	// 18 | 19 :
+	 -1,  -1,	// 20 | 21 :
+	 -1,  -1,	// 22 | 23 :
+	 -1,  -1,	// 24 | 25 :
+	 -1,  -1,	// 26 | 27 :
+	 -1,  -1,	// 28 | 29 :
+	 -1,  -1,	// 30 | 31 :
+	// 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, 493,	//  1 |  2 : 3.3V, GPIOX.17(I2C-2_SDA)
+	494, 481,	//  3 |  4 : GPIOX.18(I2C-2_SCL), GPIOX.5
+	 -1,  -1,	//  5 |  6 : GND,
+	 -1,  -1,	//  7 |  8 :
+	 -1,  -1,	//  9 | 10 :
+	 -1,  -1,	// 11 | 12 :
+	 -1,  -1,	// 13 | 14 :
+	 -1,  -1,	// 15 | 16 :
+	 -1,  -1,	// 17 | 18 :
+	 -1,  -1,	// 19 | 20 :
+	 -1,  -1,	// 21 | 22 :
+	 -1,  -1,	// 23 | 24 :
+	 -1,  -1,	// 25 | 26 :
+	 -1,  -1,	// 27 | 28 :
+	 -1,  -1,	// 29 | 30 :
+	 -1,  -1,	// 31 | 32 :
+	 -1,  -1,	// 33 | 34 :
+	 -1,  -1,	// 35 | 36 :
+	 -1,  -1,	// 37 | 38 :
+	 -1,  -1,	// 39 | 40 :
+	// 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
+};
+
+/*----------------------------------------------------------------------------*/
+//
+// Global variable define
+//
+/*----------------------------------------------------------------------------*/
+// wiringPi Pinmap control arrary
+/*----------------------------------------------------------------------------*/
+/* GPIO mmap control */
+static volatile uint32_t *gpio;
+
+/* wiringPi Global library */
+static struct libodroid	*lib = NULL;
+
+/*----------------------------------------------------------------------------*/
+// Function prototype define
+/*----------------------------------------------------------------------------*/
+static int	gpioToGPSETReg	(int pin);
+static int	gpioToGPLEVReg	(int pin);
+static int	gpioToPUENReg	(int pin);
+static int	gpioToPUPDReg	(int pin);
+static int	gpioToShiftReg	(int pin);
+static int	gpioToGPFSELReg	(int pin);
+static int	gpioToDSReg	(int pin);
+static int	gpioToMuxReg	(int pin);
+
+/*----------------------------------------------------------------------------*/
+// wiringPi core function
+/*----------------------------------------------------------------------------*/
+static int		_getModeToGpio		(int mode, int pin);
+static int		_setDrive		(int pin, int value);
+static int		_getDrive		(int pin);
+static int		_pinMode		(int pin, int mode);
+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);
+
+/*----------------------------------------------------------------------------*/
+// board init function
+/*----------------------------------------------------------------------------*/
+static 	void init_gpio_mmap	(void);
+
+	void init_odroidhc4 	(struct libodroid *libwiring);
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Set regsiter
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToGPSETReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_OUTP_REG_OFFSET;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_OUTP_REG_OFFSET;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return  C4_GPIOX_OUTP_REG_OFFSET;
+	return	-1;
+}
+
+/*---------------------------------------------------------------------------r-*/
+//
+// offset to the GPIO Input regsiter
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToGPLEVReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_INP_REG_OFFSET;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_INP_REG_OFFSET;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return  C4_GPIOX_INP_REG_OFFSET;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Pull up/down enable regsiter
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToPUENReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_PUEN_REG_OFFSET;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_PUEN_REG_OFFSET;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return  C4_GPIOX_PUEN_REG_OFFSET;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Pull up/down regsiter
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToPUPDReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_PUPD_REG_OFFSET;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_PUPD_REG_OFFSET;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return	C4_GPIOX_PUPD_REG_OFFSET;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO bit
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToShiftReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  pin - C4_GPIOH_PIN_START;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  pin - C4_GPIOA_PIN_START;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return  pin - C4_GPIOX_PIN_START;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Function register
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToGPFSELReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_FSEL_REG_OFFSET;
+	if(pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_FSEL_REG_OFFSET;
+	if(pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_END)
+		return  C4_GPIOX_FSEL_REG_OFFSET;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Drive Strength register
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToDSReg (int pin)
+{
+	if (pin >= C4_GPIOH_PIN_START && pin <= C4_GPIOH_PIN_END)
+		return  C4_GPIOH_DS_REG_3A_OFFSET;
+	if (pin >= C4_GPIOA_PIN_START && pin <= C4_GPIOA_PIN_END)
+		return  C4_GPIOA_DS_REG_5A_OFFSET;
+	if (pin >= C4_GPIOX_PIN_START && pin <= C4_GPIOX_PIN_MID)
+		return  C4_GPIOX_DS_REG_2A_OFFSET;
+	if (pin > C4_GPIOX_PIN_MID && pin <= C4_GPIOX_PIN_END)
+		return  C4_GPIOX_DS_REG_2B_OFFSET;
+	return	-1;
+}
+
+/*----------------------------------------------------------------------------*/
+//
+// offset to the GPIO Pin Mux register
+//
+/*----------------------------------------------------------------------------*/
+static int gpioToMuxReg (int pin)
+{
+	switch (pin) {
+	case	C4_GPIOH_PIN_START	...C4_GPIOH_PIN_END:
+		return  C4_GPIOH_MUX_B_REG_OFFSET;
+	case	C4_GPIOA_PIN_START	...C4_GPIOA_PIN_START + 7:
+		return  C4_GPIOA_MUX_D_REG_OFFSET;
+	case	C4_GPIOA_PIN_START + 8	...C4_GPIOA_PIN_END:
+		return  C4_GPIOA_MUX_E_REG_OFFSET;
+	case	C4_GPIOX_PIN_START	...C4_GPIOX_PIN_START + 7:
+		return  C4_GPIOX_MUX_3_REG_OFFSET;
+	case	C4_GPIOX_PIN_START + 8	...C4_GPIOX_PIN_START + 15:
+		return  C4_GPIOX_MUX_4_REG_OFFSET;
+	case	C4_GPIOX_PIN_START + 16	...C4_GPIOX_PIN_END:
+		return  C4_GPIOX_MUX_5_REG_OFFSET;
+	default:
+		return -1;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+static int _getModeToGpio (int mode, int pin)
+{
+	int retPin = -1;
+
+	switch (mode) {
+	/* Native gpio number */
+	case	MODE_GPIO:
+		retPin = pin;
+		break;
+	/* Native gpio number for sysfs */
+	case	MODE_GPIO_SYS:
+		retPin = lib->sysFds[pin] != -1 ? pin : -1;
+		break;
+	/* wiringPi number */
+	case	MODE_PINS:
+		retPin = pin < 64 ? pinToGpio[pin] : -1;
+		break;
+	/* header pin number */
+	case	MODE_PHYS:
+		retPin = pin < 64 ? phyToGpio[pin] : -1;
+		break;
+	default	:
+		msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+		return -1;
+	}
+
+	return retPin;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _setDrive (int pin, int value)
+{
+	int ds, shift;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	if (value < 0 || value > 3) {
+		msg(MSG_WARN, "%s : Invalid value %d (Must be 0 ~ 3)\n", __func__, value);
+		return -1;
+	}
+
+	ds    = gpioToDSReg(pin);
+	shift = gpioToShiftReg(pin);
+	shift = pin > C4_GPIOX_PIN_MID ? (shift - 16) * 2 : shift * 2;
+
+	*(gpio + ds) &= ~(0b11 << shift);
+	*(gpio + ds) |= (value << shift);
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _getDrive (int pin)
+{
+	int ds, shift;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	ds    = gpioToDSReg(pin);
+	shift = gpioToShiftReg(pin);
+	shift = pin > C4_GPIOX_PIN_MID ? (shift - 16) * 2 : shift * 2;
+
+	return (*(gpio + ds)	>> shift) & 0b11;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _pinMode (int pin, int mode)
+{
+	int fsel, shift, origPin = pin;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	softPwmStop  (origPin);
+	softToneStop (origPin);
+
+	fsel  = gpioToGPFSELReg(pin);
+	shift = gpioToShiftReg (pin);
+
+	switch (mode) {
+	case	INPUT:
+		*(gpio + fsel) = (*(gpio + fsel) | (1 << shift));
+		break;
+	case	OUTPUT:
+		*(gpio + fsel) = (*(gpio + fsel) & ~(1 << shift));
+		break;
+	case	SOFT_PWM_OUTPUT:
+		softPwmCreate (pin, 0, 100);
+		break;
+	case	SOFT_TONE_OUTPUT:
+		softToneCreate (pin);
+		break;
+	default:
+		msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _getAlt (int pin)
+{
+	int fsel, mux, shift, target, mode;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return	-1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return	-1;
+
+	fsel   = gpioToGPFSELReg(pin);
+	mux    = gpioToMuxReg(pin);
+	target = shift = gpioToShiftReg(pin);
+
+	while (target >= 8) {
+		target -= 8;
+	}
+
+	mode = (*(gpio + mux) >> (target * 4)) & 0xF;
+	return	mode ? mode + 1 : (*(gpio + fsel) & (1 << shift)) ? 0 : 1;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _getPUPD (int pin)
+{
+	int puen, pupd, shift;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	puen  = gpioToPUENReg(pin);
+	pupd  = gpioToPUPDReg(pin);
+	shift = gpioToShiftReg(pin);
+
+	if (*(gpio + puen) & (1 << shift))
+		return *(gpio + pupd) & (1 << shift) ? 1 : 2;
+	else
+		return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _pullUpDnControl (int pin, int pud)
+{
+	int shift = 0;
+
+	if (lib->mode == MODE_GPIO_SYS)
+		return -1;
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	shift = gpioToShiftReg(pin);
+
+	if (pud) {
+		// Enable Pull/Pull-down resister
+		*(gpio + gpioToPUENReg(pin)) =
+			(*(gpio + gpioToPUENReg(pin)) | (1 << shift));
+
+		if (pud == PUD_UP)
+			*(gpio + gpioToPUPDReg(pin)) =
+				(*(gpio + gpioToPUPDReg(pin)) |  (1 << shift));
+		else
+			*(gpio + gpioToPUPDReg(pin)) =
+				(*(gpio + gpioToPUPDReg(pin)) & ~(1 << shift));
+	} else	// Disable Pull/Pull-down resister
+		*(gpio + gpioToPUENReg(pin)) =
+			(*(gpio + gpioToPUENReg(pin)) & ~(1 << shift));
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _digitalRead (int pin)
+{
+	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;
+
+	if ((*(gpio + gpioToGPLEVReg(pin)) & (1 << gpioToShiftReg(pin))) != 0)
+		return HIGH ;
+	else
+		return LOW ;
+}
+
+/*----------------------------------------------------------------------------*/
+static int _digitalWrite (int pin, int value)
+{
+	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_WARN, "%s: Failed with reading from sysfs GPIO node. \n", __func__);
+			} else {
+				if (write(lib->sysFds[pin], "1\n", 2) < 0)
+					msg(MSG_WARN, "%s: Failed with reading from sysfs GPIO node. \n", __func__);
+			}
+		}
+		return -1;
+	}
+
+	if ((pin = _getModeToGpio(lib->mode, pin)) < 0)
+		return -1;
+
+	if (value == LOW)
+		*(gpio + gpioToGPSETReg(pin)) &= ~(1 << gpioToShiftReg(pin));
+	else
+		*(gpio + gpioToGPSETReg(pin)) |=  (1 << gpioToShiftReg(pin));
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static void init_gpio_mmap (void)
+{
+	int fd = -1;
+	void *mapped;
+
+	/* 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 {
+		// #define C4_GPIO_BASE		0xff634000
+#ifdef ANDROID
+#if defined(__aarch64__)
+		mapped = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, C4_GPIO_BASE);
+#else
+		mapped = mmap64(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off64_t)C4_GPIO_BASE);
+#endif
+#else
+		mapped = mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, C4_GPIO_BASE);
+#endif
+
+		if (mapped == MAP_FAILED)
+			msg(MSG_ERR, "wiringPiSetup: mmap (GPIO) failed: %s \n", strerror (errno));
+		else
+			gpio = (uint32_t *) mapped;
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+void init_odroidhc4 (struct libodroid *libwiring)
+{
+	init_gpio_mmap();
+
+	/* wiringPi Core function initialize */
+	libwiring->getModeToGpio	= _getModeToGpio;
+	libwiring->setDrive		= _setDrive;
+	libwiring->getDrive		= _getDrive;
+	libwiring->pinMode		= _pinMode;
+	libwiring->getAlt		= _getAlt;
+	libwiring->getPUPD		= _getPUPD;
+	libwiring->pullUpDnControl	= _pullUpDnControl;
+	libwiring->digitalRead		= _digitalRead;
+	libwiring->digitalWrite		= _digitalWrite;
+
+	/* specify pin base number */
+	libwiring->pinBase		= C4_GPIO_PIN_BASE;
+
+	/* global variable setup */
+	lib = libwiring;
+}
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------------------------------------------------------*/
diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c
index 768298a..29bc162 100644
--- a/wiringPi/wiringPi.c
+++ b/wiringPi/wiringPi.c
@@ -60,6 +60,7 @@
 	"ODROID-N1",
 	"ODROID-N2/N2Plus",
 	"ODROID-C4",
+	"ODROID-HC4",
 };
 
 const char *piRevisionNames [16] =
@@ -501,6 +502,11 @@
 			libwiring.mem = 4;
 			libwiring.rev = 1;
 			break;
+		case MODEL_ODROID_HC4:
+			libwiring.maker = MAKER_AMLOGIC;
+			libwiring.mem = 4;
+			libwiring.rev = 1;
+			break;
 		case MODEL_UNKNOWN:
 		default:
 			libwiring.model = MAKER_UNKNOWN;
@@ -1208,6 +1214,9 @@
 	case MODEL_ODROID_C4:
 		init_odroidc4(&libwiring);
 	break;
+	case MODEL_ODROID_HC4:
+		init_odroidhc4(&libwiring);
+	break;
 	default:
 		return wiringPiFailure (WPI_ALMOST,
 			"wiringPiSetup: Unknown model\n");
diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h
index e02778b..0f72ec7 100644
--- a/wiringPi/wiringPi.h
+++ b/wiringPi/wiringPi.h
@@ -59,6 +59,7 @@
 #define	MODEL_ODROID_N1		4
 #define	MODEL_ODROID_N2		5
 #define	MODEL_ODROID_C4		6
+#define	MODEL_ODROID_HC4	7
 
 #define	MAKER_UNKNOWN		0
 #define	MAKER_AMLOGIC		1
diff --git a/wiringPi/wiringPiI2C.c b/wiringPi/wiringPiI2C.c
index bb948fa..43161d7 100644
--- a/wiringPi/wiringPiI2C.c
+++ b/wiringPi/wiringPiI2C.c
@@ -260,6 +260,7 @@
 	break;
 	case MODEL_ODROID_N2:
 	case MODEL_ODROID_C4:
+	case MODEL_ODROID_HC4:
 		if (cmpKernelVersion(KERN_NUM_TO_REVISION, 4, 9, 230))
 			device = "/dev/i2c-0";
 		else
diff --git a/wiringPi/wiringPiSPI.c b/wiringPi/wiringPiSPI.c
index e860ed6..e2122bb 100644
--- a/wiringPi/wiringPiSPI.c
+++ b/wiringPi/wiringPiSPI.c
@@ -138,12 +138,13 @@
 
 	piBoardId (&model, &temp, &temp, &temp, &temp) ;
 
-	if (model == MODEL_ODROID_C2) {
+	switch(model)	{
+	case MODEL_ODROID_C2:
 		return wiringPiFailure (WPI_ALMOST,
 			"ODROID C2 does not support hardware SPI. Check out the SPI bitbang and use wiringPiSPISetupInterface.\n");
-	}
-
-	switch(model)	{
+	case MODEL_ODROID_HC4:
+		return wiringPiFailure (WPI_ALMOST,
+			"ODROID HC4 does not support hardware SPI.\n");
 	case MODEL_ODROID_C1:
 	case MODEL_ODROID_N2:
 	case MODEL_ODROID_C4: