From ffeba7f622e6ff7d3fa39ff0e014d429604129b1 Mon Sep 17 00:00:00 2001 From: Yang Deokgyu Date: Mon, 1 Jul 2019 17:22:01 +0900 Subject: [PATCH] ODROID-C4: New support for ODROID-C4 Signed-off-by: Yang Deokgyu Signed-off-by: Luke Go Change-Id: I0fd2de97d95bb8bf71cc1975081177571b1e8301 --- Android.bp | 1 + build | 2 +- debian/odroid-wiringpi.postinst | 2 +- gpio/readall.c | 68 +++ pins/Makefile | 3 +- pins/odroid_c4.pdf | Bin 0 -> 31518 bytes pins/odroid_c4.tex | 99 +++++ wiringPi/Makefile | 4 +- wiringPi/odroidc4.c | 704 ++++++++++++++++++++++++++++++++ wiringPi/odroidc4.h | 65 +++ wiringPi/wiringPi.c | 11 + wiringPi/wiringPi.h | 1 + wiringPi/wiringPiI2C.c | 1 + wiringPi/wiringPiSPI.c | 1 + 14 files changed, 958 insertions(+), 4 deletions(-) create mode 100644 pins/odroid_c4.pdf create mode 100644 pins/odroid_c4.tex create mode 100644 wiringPi/odroidc4.c create mode 100644 wiringPi/odroidc4.h diff --git a/Android.bp b/Android.bp index 098a4ed..28fec7f 100644 --- a/Android.bp +++ b/Android.bp @@ -24,6 +24,7 @@ cc_library_shared { "wiringPi/wiringPi.c", "wiringPi/mcp23017.c", "wiringPi/odroidc2.c", + "wiringPi/odroidc4.c", "wiringPi/drcSerial.c", "wiringPi/mcp23s08.c", "wiringPi/odroidn1.c", diff --git a/build b/build index bfe05dc..37978ab 100755 --- a/build +++ b/build @@ -64,7 +64,7 @@ configure_gpiomem() { *c|*c1|*c2) $sudo cp -f udev/rules.d/99-odroid-wiringpi-meson.rules /etc/udev/rules.d/ ;; - *n2) + *n2|*c4) $sudo cp -f udev/rules.d/99-odroid-wiringpi-aml.rules /etc/udev/rules.d/ ;; *) diff --git a/debian/odroid-wiringpi.postinst b/debian/odroid-wiringpi.postinst index 83c66ec..c02cfba 100644 --- a/debian/odroid-wiringpi.postinst +++ b/debian/odroid-wiringpi.postinst @@ -20,7 +20,7 @@ case "$1" in *c|*c1|*c2) cp -f /tmp/odroid-wiringpi/rules.d/99-odroid-wiringpi-meson.rules /etc/udev/rules.d/ ;; - *n2) + *n2|*c4) cp -f /tmp/odroid-wiringpi/rules.d/99-odroid-wiringpi-aml.rules /etc/udev/rules.d/ ;; *) diff --git a/gpio/readall.c b/gpio/readall.c index be2619c..83ad11c 100755 --- a/gpio/readall.c +++ b/gpio/readall.c @@ -481,6 +481,68 @@ static const char *physNamesOdroidN2 [64] = NULL,NULL,NULL, }; +/*----------------------------------------------------------------------------*/ +static const char *physNamesOdroidC4All [64] = +{ + NULL, + + " 3.3V", "5V ", + " SDA.2", "5V ", + " SCL.2", "GND(0V) ", + "GPIO.481", "TxD1 ", + " GND(0V)", "RxD1 ", + "GPIO.479", "GPIO.492", + "GPIO.480", "GND(0V) ", + "GPIO.483", "GPIO.476", + " 3.3V", "GPIO.477", + " MOSI", "GND(0V) ", + " MISO", "GPIO.478", + " SLCK", "SS ", + " GND(0V)", "GPIO. 23", + " SDA.3", "SCL.3 ", + "GPIO.490", "GND(0V) ", + "GPIO.491", "GPIO. 24", + "GPIO.482", "GND(0V) ", + "GPIO.495", "GPIO. 22", + " AIN.2", "1V8 ", + " GND(0V)", "AIN.0 ", + + 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 *physNamesOdroidC4 [64] = +{ + NULL, + + " 3.3V", "5V ", + " SDA.2", "5V ", + " SCL.2", "0V ", + " IO.481", "TxD1 ", + " 0V", "RxD1 ", + " IO.479", "IO.492 ", + " IO.480", "0V ", + " IO.483", "IO.476 ", + " 3.3V", "IO.477 ", + " MOSI", "0V ", + " MISO", "IO.478 ", + " SLCK", "SS ", + " 0V", "IO. 23 ", + " SDA.3", "SCL.3 ", + " IO.490", "0V ", + " IO.491", "IO. 24 ", + " IO.482", "0V ", + " IO.495", "IO. 22 ", + " AIN.2", "1V8 ", + " 0V", "AIN.0 ", + + 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 ; @@ -533,6 +595,7 @@ static void readallPhys(int model, int UNU rev, int physPin, const char *physNam break; case MODEL_ODROID_XU3: case MODEL_ODROID_N2: + case MODEL_ODROID_C4: printf (" | %2d | %5s", getPadDrive(pin), pupd[getPUPD(pin)]); break; default: @@ -573,6 +636,7 @@ static void readallPhys(int model, int UNU rev, int physPin, const char *physNam break; case MODEL_ODROID_XU3: case MODEL_ODROID_N2: + case MODEL_ODROID_C4: printf (" | %-5s | %-2d", pupd[getPUPD(pin)], getPadDrive(pin)); break; default: @@ -696,6 +760,10 @@ void doReadall(int argc, char *argv[]) { headerName = (isAll == FALSE) ? "--- N2 ---" : "---- Model ODROID-N2 ----"; physNames = (char *) ((isAll == FALSE) ? physNamesOdroidN2 : physNamesOdroidN2All); break; + case MODEL_ODROID_C4: + headerName = (isAll == FALSE) ? "--- C4 ---" : "---- Model ODROID-C4 ----"; + physNames = (char *) ((isAll == FALSE) ? physNamesOdroidC4 : physNamesOdroidC4All); + break; default: printf("Oops - unknown model: %d\n", model); return; diff --git a/pins/Makefile b/pins/Makefile index 0426542..665f769 100755 --- a/pins/Makefile +++ b/pins/Makefile @@ -8,7 +8,8 @@ SRC = pins.tex \ odroid_c1.tex \ odroid_c2.tex \ odroid_n1.tex \ - odroid_n2.tex + odroid_n2.tex \ + odroid_c4.tex DVI = $(SRC:.tex=.dvi) diff --git a/pins/odroid_c4.pdf b/pins/odroid_c4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d5d4760363f2f8e9b655bd19d785ec760904ced4 GIT binary patch literal 31518 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#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 + 479, 492, // 0 | 1 : GPIOX.3, GPIOX.16 + 480, 483, // 2 | 3 : GPIOX.4, GPIOX.7 + 476, 477, // 4 | 5 : GPIOX.0, GPIOX.1 + 478, 481, // 6 | 7 : GPIOX.2, GPIOX.5 + 493, 494, // 8 | 9 : GPIOX.17(I2C-2_SDA), GPIOX.18(I2C-2_SCL) + 486, 23, // 10 | 11 : GPIOX.10(SPI_SS), GPIOH.6 + 484, 485, // 12 | 13 : GPIOX.8(SPI_MOSI), GPIOX.9(SPI_MISO) + 487, 488, // 14 | 15 : GPIOX.11(SPI_CLK), GPIOX.12(UART_TX_B) + 489, -1, // 16 | 17 : GPIOX.13(UART_RX_B), + -1, - 1, // 18 | 19 : + -1, 490, // 20 | 21 : , GPIOX.14 + 491, 482, // 22 | 23 : GPIOX.15, GPIOX.6 + 495, -1, // 24 | 25 : GPIOX.19, ADC.AIN3 + 24, 22, // 26 | 27 : GPIOH.7, GPIOH.5 + -1, - 1, // 28 | 29 : REF1.8V OUT, ADC.AIC4 + 474, 475, // 30 | 31 : GPIOA.14(I2C-3_SDA), GPIOA.15(I2C-3_SCL) + // 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 + 493, -1, // 3 | 4 : GPIOX.17(I2C-2_SDA), 5.0V + 494, -1, // 5 | 6 : GPIOX.18(I2C-2_SCL), GND + 481, 488, // 7 | 8 : GPIOX.5, GPIOX.12(UART_TX_B) + -1, 489, // 9 | 10 : GND, GPIOX.13(UART_RX_B) + 479, 492, // 11 | 12 : GPIOX.3, GPIOX.16 + 480, -1, // 13 | 14 : GPIOX.4, GND + 483, 476, // 15 | 16 : GPIOX.7, GPIOX.0 + -1, 477, // 17 | 18 : 3.3V, GPIOX.1 + 484, -1, // 19 | 20 : GPIOX.8(SPI_MOSI), GND + 485, 478, // 21 | 22 : GPIOX.9(SPI_MISO), GPIOX.2 + 487, 486, // 23 | 24 : GPIOX.11(SPI_CLK), GPIOX.10(SPI_SS) + -1, 23, // 25 | 26 : GND, GPIOH.6 + 474, 475, // 27 | 28 : GPIOA.14(I2C-3_SDA), GPIOA.15(I2C-3_SCL) + 490, -1, // 29 | 30 : GPIOX.14, GND + 491, 24, // 31 | 32 : GPIOX.15, GPIOH.7 + 482, -1, // 33 | 34 : GPIOX.6, GND + 495, 22, // 35 | 36 : GPIOX.19, GPIOH.5 + -1, -1, // 37 | 38 : ADC.AIN3, 1.8V REF OUT + -1, -1, // 39 | 40 : GND, ADC.AIC4 + // 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 +/*----------------------------------------------------------------------------*/ +/* ADC file descriptor */ +static int adcFds[2]; + +/* 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 _setPadDrive (int pin, int value); +static int _getPadDrive (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); +static int _analogRead (int pin); +static int _digitalWriteByte (const unsigned int value); +static unsigned int _digitalReadByte (void); + +/*----------------------------------------------------------------------------*/ +// board init function +/*----------------------------------------------------------------------------*/ +static void init_gpio_mmap (void); +static void init_adc_fds (void); + + void init_odroidc4 (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 _setPadDrive (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 _getPadDrive (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 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) { + case 3: case 25: + pin = 0; + break; + case 2: case 29: + pin = 1; + break; + 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 gpiox; + + if (lib->mode == MODE_GPIO_SYS) + return -1; + + gpiox.wvalue = *(gpio + C4_GPIOX_INP_REG_OFFSET); + + /* Wiring PI GPIO0 = C4 GPIOX.3 */ + gpiox.bits.bit3 = (value & 0x01); + /* Wiring PI GPIO1 = C4 GPIOX.16 */ + gpiox.bits.bit16 = (value & 0x02); + /* Wiring PI GPIO2 = C4 GPIOX.4 */ + gpiox.bits.bit4 = (value & 0x04); + /* Wiring PI GPIO3 = C4 GPIOX.7 */ + gpiox.bits.bit7 = (value & 0x08); + /* Wiring PI GPIO4 = C4 GPIOX.0 */ + gpiox.bits.bit0 = (value & 0x10); + /* Wiring PI GPIO5 = C4 GPIOX.1 */ + gpiox.bits.bit1 = (value & 0x20); + /* Wiring PI GPIO6 = C4 GPIOX.2 */ + gpiox.bits.bit2 = (value & 0x40); + /* Wiring PI GPIO7 = C4 GPIOX.5 */ + gpiox.bits.bit5 = (value & 0x80); + + *(gpio + C4_GPIOX_OUTP_REG_OFFSET) = gpiox.wvalue; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static unsigned int _digitalReadByte (void) +{ + union reg_bitfield gpiox; + unsigned int value = 0; + + if (lib->mode == MODE_GPIO_SYS) + return -1; + + gpiox.wvalue = *(gpio + C4_GPIOX_INP_REG_OFFSET); + + /* Wiring PI GPIO0 = C4 GPIOX.3 */ + if (gpiox.bits.bit3) + value |= 0x01; + /* Wiring PI GPIO1 = C4 GPIOX.16 */ + if (gpiox.bits.bit16) + value |= 0x02; + /* Wiring PI GPIO2 = C4 GPIOX.4 */ + if (gpiox.bits.bit4) + value |= 0x04; + /* Wiring PI GPIO3 = C4 GPIOX.7 */ + if (gpiox.bits.bit7) + value |= 0x08; + /* Wiring PI GPIO4 = C4 GPIOX.0 */ + if (gpiox.bits.bit0) + value |= 0x10; + /* Wiring PI GPIO5 = C4 GPIOX.1 */ + if (gpiox.bits.bit1) + value |= 0x20; + /* Wiring PI GPIO6 = C4 GPIOX.2 */ + if (gpiox.bits.bit2) + value |= 0x40; + /* Wiring PI GPIO7 = C4 GPIOX.5 */ + if (gpiox.bits.bit5) + value |= 0x80; + + return value; +} + +/*----------------------------------------------------------------------------*/ +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)); + } 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; + } +} + +/*----------------------------------------------------------------------------*/ +static void init_adc_fds (void) +{ + const char *AIN25_NODE, *AIN29_NODE; + + /* ADC node setup */ + AIN25_NODE = "/sys/devices/platform/ff809000.saradc/iio:device0/in_voltage2_raw"; + AIN29_NODE = "/sys/devices/platform/ff809000.saradc/iio:device0/in_voltage0_raw"; + + adcFds[0] = open(AIN25_NODE, O_RDONLY); + adcFds[1] = open(AIN29_NODE, O_RDONLY); +} + +/*----------------------------------------------------------------------------*/ +void init_odroidc4 (struct libodroid *libwiring) +{ + init_gpio_mmap(); + + init_adc_fds(); + + /* wiringPi Core function initialize */ + libwiring->getModeToGpio = _getModeToGpio; + libwiring->setPadDrive = _setPadDrive; + libwiring->getPadDrive = _getPadDrive; + libwiring->pinMode = _pinMode; + libwiring->getAlt = _getAlt; + libwiring->getPUPD = _getPUPD; + libwiring->pullUpDnControl = _pullUpDnControl; + libwiring->digitalRead = _digitalRead; + libwiring->digitalWrite = _digitalWrite; + libwiring->analogRead = _analogRead; + libwiring->digitalWriteByte = _digitalWriteByte; + libwiring->digitalReadByte = _digitalReadByte; + + /* specify pin base number */ + libwiring->pinBase = C4_GPIO_PIN_BASE; + + /* global variable setup */ + lib = libwiring; +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ diff --git a/wiringPi/odroidc4.h b/wiringPi/odroidc4.h new file mode 100644 index 0000000..94fb88e --- /dev/null +++ b/wiringPi/odroidc4.h @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------*/ +/* + + WiringPi ODROID-C4 Board Header file + + */ +/*----------------------------------------------------------------------------*/ +#ifndef __ODROID_C4_H__ +#define __ODROID_C4_H__ + +/*----------------------------------------------------------------------------*/ +#define C4_GPIO_BASE 0xFF634000 + +#define C4_GPIO_PIN_BASE 460 + +#define C4_GPIOH_PIN_START 17 // GPIOH_0 +#define C4_GPIOH_PIN_END 25 // GPIOH_8 +#define C4_GPIOA_PIN_START C4_GPIO_PIN_BASE // GPIOA_0 +#define C4_GPIOA_PIN_END (C4_GPIO_PIN_BASE + 15) // GPIOA_15 +#define C4_GPIOX_PIN_START (C4_GPIO_PIN_BASE + 16) // GPIOX_0 +#define C4_GPIOX_PIN_MID (C4_GPIO_PIN_BASE + 31) // GPIOX_15 +#define C4_GPIOX_PIN_END (C4_GPIO_PIN_BASE + 35) // GPIOX_19 + +#define C4_GPIOH_FSEL_REG_OFFSET 0x119 +#define C4_GPIOH_OUTP_REG_OFFSET 0x11A +#define C4_GPIOH_INP_REG_OFFSET 0x11B +#define C4_GPIOH_PUPD_REG_OFFSET 0x13D +#define C4_GPIOH_PUEN_REG_OFFSET 0x14B +#define C4_GPIOH_DS_REG_3A_OFFSET 0x1D4 +#define C4_GPIOH_MUX_B_REG_OFFSET 0x1BB + +#define C4_GPIOA_FSEL_REG_OFFSET 0x120 +#define C4_GPIOA_OUTP_REG_OFFSET 0x121 +#define C4_GPIOA_INP_REG_OFFSET 0x122 +#define C4_GPIOA_PUPD_REG_OFFSET 0x13F +#define C4_GPIOA_PUEN_REG_OFFSET 0x14D +#define C4_GPIOA_DS_REG_5A_OFFSET 0x1D6 +#define C4_GPIOA_MUX_D_REG_OFFSET 0x1BD +#define C4_GPIOA_MUX_E_REG_OFFSET 0x1BE + +#define C4_GPIOX_FSEL_REG_OFFSET 0x116 +#define C4_GPIOX_OUTP_REG_OFFSET 0x117 +#define C4_GPIOX_INP_REG_OFFSET 0x118 +#define C4_GPIOX_PUPD_REG_OFFSET 0x13C +#define C4_GPIOX_PUEN_REG_OFFSET 0x14A +#define C4_GPIOX_DS_REG_2A_OFFSET 0x1D2 +#define C4_GPIOX_DS_REG_2B_OFFSET 0x1D3 +#define C4_GPIOX_MUX_3_REG_OFFSET 0x1B3 +#define C4_GPIOX_MUX_4_REG_OFFSET 0x1B4 +#define C4_GPIOX_MUX_5_REG_OFFSET 0x1B5 + +#ifdef __cplusplus +extern "C" { +#endif + +extern void init_odroidc4 (struct libodroid *libwiring); + +#ifdef __cplusplus +} +#endif + +/*----------------------------------------------------------------------------*/ +#endif /* __ODROID_C4_H__ */ +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index 54d0484..9f658e8 100755 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -37,6 +37,7 @@ #include "odroidxu3.h" #include "odroidn1.h" #include "odroidn2.h" +#include "odroidc4.h" /*----------------------------------------------------------------------------*/ // Const string define @@ -49,6 +50,7 @@ const char *piModelNames [16] = "ODROID-XU3/4", "ODROID-N1", "ODROID-N2", + "ODROID-C4", }; const char *piRevisionNames [16] = @@ -441,6 +443,11 @@ int piGpioLayout (void) libwiring.maker = MAKER_AMLOGIC; libwiring.mem = 4; libwiring.rev = 1; + } else if (strncmp (c, "05", 2) == 0) { + libwiring.model = MODEL_ODROID_C4; + libwiring.maker = MAKER_AMLOGIC; + libwiring.mem = 4; + libwiring.rev = 1; } else { libwiring.model = MODEL_UNKNOWN; libwiring.maker = MAKER_UNKNOWN; @@ -474,6 +481,7 @@ int piBoardRev (void) * Rev 1.1 : /sys/class/odroid/boardrev value is 1 (Mass board) * 03xx - Model ODROID N1, 4096M, Hardkernel * 04xx - Model ODROID N2, 4096M, Hardkernel + * 05xx - Model ODROID C4, 4096M, Hardkernel */ /*----------------------------------------------------------------------------*/ void piBoardId (int *model, int *rev, int *mem, int *maker, int *warranty) @@ -1073,6 +1081,9 @@ int wiringPiSetup (void) case MODEL_ODROID_N2: init_odroidn2(&libwiring); break; + case MODEL_ODROID_C4: + init_odroidc4(&libwiring); + break; default: return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: Unknown model\n"); diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index 25d8dd8..a00dc9d 100755 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -52,6 +52,7 @@ #define MODEL_ODROID_XU3 3 #define MODEL_ODROID_N1 4 #define MODEL_ODROID_N2 5 +#define MODEL_ODROID_C4 6 #define MAKER_UNKNOWN 0 #define MAKER_AMLOGIC 1 diff --git a/wiringPi/wiringPiI2C.c b/wiringPi/wiringPiI2C.c index b9fc54a..48d0a1e 100644 --- a/wiringPi/wiringPiI2C.c +++ b/wiringPi/wiringPiI2C.c @@ -251,6 +251,7 @@ int wiringPiI2CSetup (const int devId) device = "/dev/i2c-4"; break; case MODEL_ODROID_N2: + case MODEL_ODROID_C4: device = "/dev/i2c-2"; break; } diff --git a/wiringPi/wiringPiSPI.c b/wiringPi/wiringPiSPI.c index 5166383..406f099 100644 --- a/wiringPi/wiringPiSPI.c +++ b/wiringPi/wiringPiSPI.c @@ -146,6 +146,7 @@ int wiringPiSPISetupMode (int channel, int speed, int mode) switch(model) { case MODEL_ODROID_C1: case MODEL_ODROID_N2: + case MODEL_ODROID_C4: sprintf(device, "%s%d", spiDevType0, channel); break; case MODEL_ODROID_XU3: