All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.internal.soc.rockchip.RK3399MmapGpio Maven / Gradle / Ivy

The newest version!
package com.diozero.internal.soc.rockchip;

/*-
 * #%L
 * Organisation: diozero
 * Project:      diozero - Core
 * Filename:     RK3399MmapGpio.java
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at https://www.diozero.com/.
 * %%
 * Copyright (C) 2016 - 2024 diozero
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.tinylog.Logger;

import com.diozero.api.DeviceMode;
import com.diozero.api.GpioPullUpDown;
import com.diozero.internal.spi.MmapGpioInterface;
import com.diozero.util.MmapIntBuffer;
import com.diozero.util.SleepUtil;

/*-
 * Wiki: https://wiki.radxa.com/Rockpi4/hardware/gpio
 * Datasheet: https://dl.radxa.com/rockpi4/docs/hw/datasheets/Rockchip%20RK3399-T%20Datasheet%20V1.0-20210818.pdf
 * WiringX: https://github.com/wiringX/wiringX/blob/master/src/soc/rockchip/rk3399.c
 *
 * Rockchip RK3399 GPIO has 5 banks, GPIO0 to GPIO4, each bank has 32pins.
 *
 * For Rockchip 4.4 kernel, the GPIO number can be calculated as below, take GPIO4_D5 (PIN22 on 40PIN GPIO) as an example:
 * GPIO4_D5 = 4*32 + 3*8 + 5 = 157
 * (A=0, B=1, C=2, D=3)
 * 
 * gpio0@ff720000
 * gpio1@ff730000
 * gpio2@ff780000
 * gpio3@ff788000
 * gpio4@ff790000
 * 
 * +-----+----------+------+---+--------+----------+--------+---+------+----------+-----+
 * + GP# +     Name + Mode + V +  gpiod + Physical + gpiod  + V + Mode + Name     + GP# +
 * +-----+----------+------+---+--------+----------+--------+---+------+----------+-----+
 * |     |      3v3 |      |   |        |  1 || 2  |        |   |      | 5v       |     |
 * |  71 | GPIO2_A7 |   In | 0 |  2:7   |  3 || 4  |        |   |      | 5v       |     |
 * |  72 | GPIO2_B0 |   In | 0 |  2:8   |  5 || 6  |        |   |      | GND      |     |
 * |  75 | GPIO2_B3 |   In | 0 |  2:11  |  7 || 8  |  4:20  | 1 | UART | TXD2     | 148 |
 * |     |      GND |      |   |        |  9 || 10 |  4:19  | 1 | UART | RXD2     | 147 |
 * | 146 |     PWM0 |   In | 0 |  4:18  | 11 || 12 |  4:3   | 1 | I2S  | GPIO4_A3 | 131 |
 * | 150 |     PWM1 |   In | 0 |  4:22  | 13 || 14 |        |   |      | GND      |     |
 * | 149 | GPIO4_C5 |   In | 0 |  4:21  | 15 || 16 |  4:26  | 1 | In   | GPIO4_D2 | 154 |
 * |     |      3v3 |      |   |        | 17 || 18 |  4:28  | 0 | In   | GPIO4_D4 | 156 |
 * |  40 |  SPI1_TX |  SPI | 0 |  1:8   | 19 || 20 |        |   |      | GND      |     |
 * |  39 |  SPI1_RX |  SPI | 1 |  1:7   | 21 || 22 |  4:29  | 0 | In   | GPIO4_D5 | 157 |
 * |  41 | SPI1_CLK |  SPI | 1 |  1:9   | 23 || 24 |  1:10  | 1 | SPI  | SPI1_CS0 | 42  |
 * |     |      GND |      |   |        | 25 || 26 |        |   | In   | ADC_IN0  | 0   |
 * |  64 | GPIO2_A0 |   In | 0 |  2:0   | 27 || 28 |  2:1   | 0 | In   | GPIO2_A1 | 65  |
 * |  74 | GPIO2_B2 |   In | 0 |  2:10  | 29 || 30 |        |   |      | GND      |     |
 * |  73 | GPIO2_B1 |   In | 0 |  2:9   | 31 || 32 |  3:0   | 0 | In   | GPIO3_C0 | 112 |
 * |  76 | GPIO2_B4 |   In | 0 |  2:12  | 33 || 34 |        |   |      | GND      |     |
 * | 133 | GPIO4_A5 |  I2S | 0 |  4:5   | 35 || 36 |  4:4   | 0 | I2S  | GPIO4_A4 | 132 |
 * | 158 | GPIO4_D6 |   In | 0 |  4:30  | 37 || 38 |  4:6   | 1 | I2S  | GPIO4_A6 | 134 |
 * |     |      GND |      |   |        | 39 || 40 |  4:7   | 0 | I2S  | GPIO4_A7 | 135 |
 * +-----+----------+------+---+--------+----------+--------+---+------+----------+-----+
 * 
 * Note: to use GPIO 112, the status LED has to be disabled (i.e. LED’s trigger set to none).
 * Otherwise, the operation will have no effect and no error will be returned.
 * This workaround is required on vendor 4.4 kernel.
 */
public class RK3399MmapGpio implements MmapGpioInterface {
	private static final String MEM_DEVICE = "/dev/mem";
	private static final int GRF_GPIO2A_IOMUX = 0x0e000;
	// GRF - General Register Files (64K)
	private static final long GRF_MEM_OFFSET = 0xff77_0000L + GRF_GPIO2A_IOMUX;
	// PMU - Power Management Unit (64K)
	private static final long PMUGRF_MEM_OFFSET = 0xff32_0000L;
	// PMU CRU - Clock & Rest Unit (64K)
	private static final long PMUCRU_MEM_OFFSET = 0xff75_0000L;
	// CRU - Clock & Rest Unit (64K)
	private static final long CRU_MEM_OFFSET = 0xff76_0000L;
	// GPIO0 (64K), GPIO1 (64K), GPIO2 (32K), GPIO3 (32K), GPIO4 (32K)
	private static final long[] GPIOMEM_OFFSETS = { 0xff72_0000L, 0xff73_0000L, 0xff78_0000L, 0xff78_8000L,
			0xff79_0000L };

	private static final int BLOCK_SIZE = 4 * 1024;

	// Data register
	private static final int GPIO_SWPORTA_DR = 0x0000 / 4;
	// Data direction register
	private static final int GPIO_SWPORTA_DDR = 0x0004 / 4;
	// External value register
	private static final int GPIO_EXT_PORTA = 0x0050 / 4;
	// GPIO Mode
	private static final int IOMUX_INT_OFFSET = 0x00000 / 4;
	// GPIO PU/PD
	private static final int PUD_INT_OFFSET = 0x00040 / 4;

	// Internal clock gating register for GPIO banks 0-1 [bits 3-4]
	private static final int PMUCRU_CLKGATE_CON1_INT_OFFSET = 0x0104 / 4;
	// Internal clock gating register for GPIO banks 2-4 [bits 3-5]
	private static final int CRU_CLKGATE_CON31_INT_OFFSET = 0x037c / 4;

	private boolean initialised;
	private MmapIntBuffer grfMmapIntBuffer;
	private MmapIntBuffer pmuGrfMmapIntBuffer;
	private MmapIntBuffer pmuCruMmapIntBuffer;
	private MmapIntBuffer cruMmapIntBuffer;
	private MmapIntBuffer[] gpioBanks;
	private Map gpioModes;
	private Map gpioModeValues;

	@Override
	public synchronized void initialise() {
		if (!initialised) {
			grfMmapIntBuffer = new MmapIntBuffer(MEM_DEVICE, GRF_MEM_OFFSET, BLOCK_SIZE, ByteOrder.LITTLE_ENDIAN);
			pmuGrfMmapIntBuffer = new MmapIntBuffer(MEM_DEVICE, PMUGRF_MEM_OFFSET, BLOCK_SIZE,
					ByteOrder.LITTLE_ENDIAN);
			pmuCruMmapIntBuffer = new MmapIntBuffer(MEM_DEVICE, PMUCRU_MEM_OFFSET, BLOCK_SIZE,
					ByteOrder.LITTLE_ENDIAN);
			cruMmapIntBuffer = new MmapIntBuffer(MEM_DEVICE, CRU_MEM_OFFSET, BLOCK_SIZE, ByteOrder.LITTLE_ENDIAN);

			gpioBanks = new MmapIntBuffer[GPIOMEM_OFFSETS.length];
			for (int i = 0; i < GPIOMEM_OFFSETS.length; i++) {
				gpioBanks[i] = new MmapIntBuffer(MEM_DEVICE, GPIOMEM_OFFSETS[i], BLOCK_SIZE,
						ByteOrder.LITTLE_ENDIAN);
			}

			gpioModes = new HashMap<>();
			gpioModeValues = new HashMap<>();
			// Only map the exposed GPIOs
			// Note conflicts between RK3399 datasheet and Wiki page
			// GPIO1_A7 (39)
			gpioModes.put("39-1", DeviceMode.SERIAL);
			gpioModeValues.put("39-" + DeviceMode.SERIAL, Integer.valueOf(1));
			gpioModes.put("39-2", DeviceMode.SPI);
			gpioModeValues.put("39-" + DeviceMode.SPI, Integer.valueOf(2));
			// GPIO1_B0 (40)
			gpioModes.put("40-1", DeviceMode.SERIAL);
			gpioModeValues.put("40-" + DeviceMode.SERIAL, Integer.valueOf(1));
			gpioModes.put("40-2", DeviceMode.SPI);
			gpioModeValues.put("40-" + DeviceMode.SPI, Integer.valueOf(2));
			// GPIO1_B1 (41)
			gpioModes.put("41-2", DeviceMode.SPI);
			gpioModeValues.put("41-" + DeviceMode.SPI, Integer.valueOf(2));
			// GPIO1_B2 (42)
			gpioModes.put("42-2", DeviceMode.SPI);
			gpioModeValues.put("42-" + DeviceMode.SPI, Integer.valueOf(2));
			// GPIO2_A0 (64)
			gpioModes.put("64-2", DeviceMode.I2C);
			gpioModeValues.put("64-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_A1 (65)
			gpioModes.put("65-2", DeviceMode.I2C);
			gpioModeValues.put("65-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_A7 (71)
			gpioModes.put("71-2", DeviceMode.I2C);
			gpioModeValues.put("71-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_B0 (72)
			gpioModes.put("72-2", DeviceMode.I2C);
			gpioModeValues.put("72-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_B1 (73)
			gpioModes.put("73-1", DeviceMode.SPI);
			gpioModeValues.put("73-" + DeviceMode.SPI, Integer.valueOf(1));
			gpioModes.put("73-2", DeviceMode.I2C);
			gpioModeValues.put("73-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_B2 (74)
			gpioModes.put("74-1", DeviceMode.SPI);
			gpioModeValues.put("74-" + DeviceMode.SPI, Integer.valueOf(1));
			gpioModes.put("74-2", DeviceMode.I2C);
			gpioModeValues.put("74-" + DeviceMode.I2C, Integer.valueOf(2));
			// GPIO2_B3 (75)
			gpioModes.put("75-1", DeviceMode.SPI);
			gpioModeValues.put("75-" + DeviceMode.SPI, Integer.valueOf(1));
			// GPIO2_B4 (76)
			gpioModes.put("76-1", DeviceMode.SPI);
			gpioModeValues.put("76-" + DeviceMode.SPI, Integer.valueOf(1));
			// GPIO3_C0 (112)
			gpioModes.put("112-2", DeviceMode.SERIAL);
			gpioModeValues.put("112-" + DeviceMode.SERIAL, Integer.valueOf(2));
			// GPIO4_A3 (131)
			gpioModes.put("131-1", DeviceMode.I2S);
			gpioModeValues.put("131-" + DeviceMode.I2S, Integer.valueOf(1));
			// GPIO4_A4 (132)
			gpioModes.put("132-1", DeviceMode.I2S);
			gpioModeValues.put("132-" + DeviceMode.I2S, Integer.valueOf(1));
			// GPIO4_A5 (133)
			gpioModes.put("133-1", DeviceMode.I2S);
			gpioModeValues.put("133-" + DeviceMode.I2S, Integer.valueOf(1));
			// GPIO4_A6 (134)
			gpioModes.put("134-1", DeviceMode.I2S);
			gpioModeValues.put("134-" + DeviceMode.I2S, Integer.valueOf(1));
			// GPIO4_A7 (135)
			gpioModes.put("135-1", DeviceMode.I2S);
			gpioModeValues.put("135-" + DeviceMode.I2S, Integer.valueOf(1));
			// GPIO4_C2 (146)
			gpioModes.put("146-1", DeviceMode.PWM_OUTPUT);
			gpioModeValues.put("146-" + DeviceMode.PWM_OUTPUT, Integer.valueOf(1));
			// GPIO4_C3 (147)
			gpioModes.put("147-1", DeviceMode.SERIAL);
			gpioModeValues.put("147-" + DeviceMode.SERIAL, Integer.valueOf(1));
			gpioModes.put("147-2", DeviceMode.SERIAL);
			gpioModeValues.put("147-" + DeviceMode.SERIAL, Integer.valueOf(2));
			// GPIO4_C4 (148)
			gpioModes.put("148-1", DeviceMode.SERIAL);
			gpioModeValues.put("148-" + DeviceMode.SERIAL, Integer.valueOf(1));
			gpioModes.put("148-2", DeviceMode.SERIAL);
			gpioModeValues.put("148-" + DeviceMode.SERIAL, Integer.valueOf(2));
			// GPIO4_C5 (149) - spdif_tx on func 1
			// GPIO4_C6 (150)
			gpioModes.put("150-1", DeviceMode.PWM_OUTPUT);
			gpioModeValues.put("150-" + DeviceMode.PWM_OUTPUT, Integer.valueOf(1));
			// GPIO4_D2 (154) - GPIO only (no iomux - bits 15:4 reserved)
			// GPIO4_D4 (156) - GPIO only (no iomux - bits 15:4 reserved)
			// GPIO4_D5 (157) - GPIO only (no iomux - bits 15:4 reserved)
			// GPIO4_D6 (158) - GPIO only (no iomux - bits 15:4 reserved)
		}
	}

	@Override
	public void close() {
		if (gpioBanks != null) {
			for (int i = 0; i < gpioBanks.length; i++) {
				gpioBanks[i].close();
			}
			gpioBanks = null;
		}
		if (pmuGrfMmapIntBuffer != null) {
			pmuGrfMmapIntBuffer.close();
			pmuGrfMmapIntBuffer = null;
		}
		if (grfMmapIntBuffer != null) {
			grfMmapIntBuffer.close();
			grfMmapIntBuffer = null;
		}
		if (cruMmapIntBuffer != null) {
			cruMmapIntBuffer.close();
			cruMmapIntBuffer = null;
		}
		if (pmuCruMmapIntBuffer != null) {
			pmuCruMmapIntBuffer.close();
			pmuCruMmapIntBuffer = null;
		}
	}

	@Override
	public DeviceMode getMode(int gpio) {
		/*-
		 * iomux control
		 * Name                Offset (16) Offset (10) GPIOs    Int Offset (16)
		 * PMUGRF_GPIO0A_IOMUX 0x00000     0           0..7     0x00000
		 * PMUGRF_GPIO0B_IOMUX 0x00004     4           8..15    0x00001
		 * PMUGRF_GPIO0C_IOMUX 0x00008     8           16..23   0x00002 - UNDEFINED
		 * PMUGRF_GPIO0D_IOMUX 0x0000c     12          24..31   0x00003 - UNDEFINED
		 * PMUGRF_GPIO1A_IOMUX 0x00010     16          32..39   0x00004
		 * PMUGRF_GPIO1B_IOMUX 0x00014     20          40..47   0x00005
		 * PMUGRF_GPIO1C_IOMUX 0x00018     24          48..55   0x00006
		 * PMUGRF_GPIO1D_IOMUX 0x0001c     28          56..63   0x00007
		 * GRF_GPIO2A_IOMUX    0x0e000     57344       64..71   0x03800
		 * GRF_GPIO2B_IOMUX    0x0e004     57348       72..79   0x03801
		 * GRF_GPIO2C_IOMUX    0x0e008     57352       80..87   0x03802
		 * GRF_GPIO2D_IOMUX    0x0e00c     57356       88..95   0x03803
		 * GRF_GPIO3A_IOMUX    0x0e010     57360       96..103  0x03804
		 * GRF_GPIO3B_IOMUX    0x0e014     57364       104..111 0x03805
		 * GRF_GPIO3C_IOMUX    0x0e018     57368       112..119 0x03806
		 * GRF_GPIO3D_IOMUX    0x0e01c     57372       120..127 0x03807
		 * GRF_GPIO4A_IOMUX    0x0e020     57376       128..135 0x03808
		 * GRF_GPIO4B_IOMUX    0x0e024     57380       136..143 0x03809
		 * GRF_GPIO4C_IOMUX    0x0e028     57384       144..151 0x0380a
		 * GRF_GPIO4D_IOMUX    0x0e02c     57388       152..159 0x0380b
		 */
		final int bank = gpio >> 5;
		final int bank_offset = gpio % 32;
		final int mode_shift = (gpio % 8) << 1;
		int mode = 0;
		// GPIOs 154 onwards are GPIO in/out only
		if (gpio < 154) {
			int mode_int_offset;
			MmapIntBuffer int_buffer;
			if (bank < 2) {
				mode_int_offset = IOMUX_INT_OFFSET + bank * 4 + bank_offset / 8;
				int_buffer = pmuGrfMmapIntBuffer;
			} else {
				mode_int_offset = IOMUX_INT_OFFSET + (bank - 2) * 4 + bank_offset / 8;
				int_buffer = grfMmapIntBuffer;
			}
			mode = (int_buffer.get(mode_int_offset) >> mode_shift) & 0b11;
		}

		if (mode == 0) {
			// GPIO - determine if input or output
			return (gpioBanks[bank].get(GPIO_SWPORTA_DDR) & (1 << bank_offset)) == 0 ? DeviceMode.DIGITAL_INPUT
					: DeviceMode.DIGITAL_OUTPUT;
		}

		return gpioModes.getOrDefault(gpio + "-" + mode, DeviceMode.UNKNOWN);
	}

	@Override
	public void setMode(int gpio, DeviceMode deviceMode) {
		int mode_mask = 0;
		// GPIOs 154 onwards are GPIO in/out only
		if (gpio < 154) {
			if (deviceMode != DeviceMode.DIGITAL_INPUT & deviceMode != DeviceMode.DIGITAL_OUTPUT) {
				Integer i = gpioModeValues.get(gpio + "-" + deviceMode);
				if (i == null) {
					throw new IllegalArgumentException("Invalid mode " + deviceMode + " for GPIO " + gpio);
				}
				mode_mask = i.intValue();
			}

			setModeUnchecked(gpio, mode_mask);
		}

		// GPIO Input or Output mode
		if (mode_mask == 0) {
			final int bank = gpio >> 5;
			final int bank_offset = gpio % 32;

			/*-
			 * cru_reg = (volatile unsigned int *)(cru_register_virtual_address + pin->cru.offset);
			 * HIGH = disable / LOW = enable the clock for the entire GPIO bank
			 * *cru_reg = REGISTER_CLEAR_BITS(cru_reg, pin->cru.bit, 1);
			 */
			int int_offset;
			MmapIntBuffer int_buffer;
			int gpio_dir_shift;
			if (bank < 2) {
				int_offset = PMUCRU_CLKGATE_CON1_INT_OFFSET;
				int_buffer = pmuCruMmapIntBuffer;
				gpio_dir_shift = bank + 3;
			} else {
				int_offset = CRU_CLKGATE_CON31_INT_OFFSET;
				int_buffer = cruMmapIntBuffer;
				gpio_dir_shift = bank + 1;
			}
			int cru_reg = int_buffer.get(int_offset);
			// Set to low to enable the clock for GPIO bank - unclear why we need to do this
			cru_reg &= ~(0b1 << gpio_dir_shift);
			// Set the write enable bit
			cru_reg |= 0b1 << (gpio_dir_shift + 16);
			int_buffer.put(int_offset, cru_reg);

			int reg_val = gpioBanks[bank].get(GPIO_SWPORTA_DDR);
			if (deviceMode == DeviceMode.DIGITAL_INPUT) {
				reg_val &= ~(0b1 << bank_offset);
			} else {
				reg_val |= 0b1 << bank_offset;
			}
			gpioBanks[bank].put(GPIO_SWPORTA_DDR, reg_val);
		}
	}

	@Override
	public void setModeUnchecked(int gpio, int mode) {
		final int bank = gpio >> 5;
		final int bank_offset = gpio % 32;
		final int mode_shift = (gpio % 8) << 1;

		int mode_int_offset;
		MmapIntBuffer int_buffer;
		if (bank < 2) {
			mode_int_offset = IOMUX_INT_OFFSET + bank * 4 + bank_offset / 8;
			int_buffer = pmuGrfMmapIntBuffer;
		} else {
			mode_int_offset = IOMUX_INT_OFFSET + (bank - 2) * 4 + bank_offset / 8;
			int_buffer = grfMmapIntBuffer;
		}

		int reg_val = int_buffer.get(mode_int_offset);
		reg_val &= ~(0b11 << mode_shift);
		// Set the write enable bits
		reg_val |= ((mode & 0b11) << mode_shift) | (0b11 << (mode_shift + 16));
		int_buffer.put(mode_int_offset, reg_val);
	}

	@Override
	public Optional getPullUpDown(int gpio) {
		final int bank = gpio >> 5;
		final int shift = (gpio % 8) << 1;
		final int bank_offset = gpio % 32;

		if (bank <= 2) {
			// PU/PD control only available for GPIO2A onwards
			return Optional.of(GpioPullUpDown.NONE);
		}

		int pud_int_offset;
		MmapIntBuffer int_buffer;
		if (bank < 2) {
			pud_int_offset = PUD_INT_OFFSET + bank * 4 + bank_offset / 8;
			int_buffer = pmuGrfMmapIntBuffer;
		} else {
			pud_int_offset = PUD_INT_OFFSET + (bank - 2) * 4 + bank_offset / 8;
			int_buffer = grfMmapIntBuffer;
		}

		final int pud_val = (int_buffer.get(pud_int_offset) >> shift) & 0b11;
		GpioPullUpDown pud;
		if (gpio < 32 || (gpio >= 80 && gpio < 96)) {
			switch (pud_val) {
			case 0b01:
				pud = GpioPullUpDown.PULL_DOWN;
				break;
			case 0b11:
				pud = GpioPullUpDown.PULL_UP;
				break;
			default:
				pud = GpioPullUpDown.NONE;
			}
		} else {
			switch (pud_val) {
			case 0b01:
				pud = GpioPullUpDown.PULL_UP;
				break;
			case 0b10:
				pud = GpioPullUpDown.PULL_DOWN;
				break;
			default:
				pud = GpioPullUpDown.NONE;
			}
		}

		return Optional.of(pud);
	}

	@Override
	public void setPullUpDown(int gpio, GpioPullUpDown pud) {
		/*-
		 * PU/PD control
		 * Name                Offset (16) Offset (10) Int Offset (16) GPIOs
		 * PMUGRF_GPIO0A_P     0x00040     64          0x00010         0..7
		 * PMUGRF_GPIO0B_P     0x00044     68          0x00011         8..15
		 * PMUGRF_GPIO0C_P     0x00048     72          0x00012         16..23
		 * PMUGRF_GPIO0D_P     0x0004c     76          0x00013         24..31
		 * PMUGRF_GPIO1A_P     0x00050     80          0x00014         32..39
		 * PMUGRF_GPIO1B_P     0x00054     84          0x00015         40..47
		 * PMUGRF_GPIO1C_P     0x00058     88          0x00016         48..55
		 * PMUGRF_GPIO1D_P     0x0005c     92          0x00017         56..63
		 * GRF_GPIO2A_P        0x0e040     96          0x03810         64..71
		 * GRF_GPIO2B_P        0x0e044     100         0x03811         72..79
		 * GRF_GPIO2C_P        0x0e048     104         0x03812         80..87
		 * GRF_GPIO2D_P        0x0e04c     108         0x03813         88..95
		 * GRF_GPIO3A_P        0x0e050     112         0x03814         96..103
		 * GRF_GPIO3B_P        0x0e054     116         0x03815         104..111
		 * GRF_GPIO3C_P        0x0e058     120         0x03816         112..119
		 * GRF_GPIO3D_P        0x0e05c     124         0x03817         120..127
		 * GRF_GPIO4A_P        0x0e060     128         0x03818         128..135
		 * GRF_GPIO4B_P        0x0e064     132         0x03819         136..143
		 * GRF_GPIO4C_P        0x0e068     136         0x0381a         144..151
		 * GRF_GPIO4D_P        0x0e06c     140         0x0381b         152..159
		 */

		final int bank = gpio >> 5;
		final int bank_offset = gpio % 32;

		final int pud_shift = (gpio % 8) << 1;
		int pud_int_offset;
		MmapIntBuffer int_buffer;
		if (bank < 2) {
			pud_int_offset = PUD_INT_OFFSET + bank * 4 + bank_offset / 8;
			int_buffer = pmuGrfMmapIntBuffer;
		} else {
			pud_int_offset = PUD_INT_OFFSET + (bank - 2) * 4 + bank_offset / 8;
			int_buffer = grfMmapIntBuffer;
		}

		int pud_val;
		if (gpio < 32 || (gpio >= 80 && gpio < 96)) {
			/*-
			 * 0a, 0b, 2c, 2d (0-4) PE/PS control:
			 * 2'b00: Z(Normal operation);
			 * 2'b01: weak 0(pull-down);
			 * 2'b10: Z(Normal operation);
			 * 2'b11: weak 1(pull-up);
			 */
			switch (pud) {
			case PULL_DOWN:
				pud_val = 0b01; // weak 0(pull-down)
				break;
			case PULL_UP:
				pud_val = 0b11; // weak 1(pull-up)
				break;
			default:
				pud_val = 0b00; // Z(Normal operation)
			}
		} else {
			/*-
			 * 1a, 1b, 1c, 1d, 2a, 2b (0-4), 3a, 3b, 3c (0-1), 3d, 4a, 4b (0-5), 4c, 4d (0-6) PU/PD control:
			 * 2'b00: Z(Normal operation);
			 * 2'b01: weak 1(pull-up);
			 * 2'b10: weak 0(pull-down);
			 * 2'b11: Reserved;
			 */
			switch (pud) {
			case PULL_UP:
				pud_val = 0b01; // weak 1(pull-up)
				break;
			case PULL_DOWN:
				pud_val = 0b10; // weak 0(pull-down)
				break;
			default:
				pud_val = 0b00; // Z(Normal operation)
			}
		}

		int reg_val = int_buffer.get(pud_int_offset);
		reg_val &= ~(0b11 << pud_shift);
		// Set the write enable bits
		reg_val |= (pud_val << pud_shift) | (0b11 << (pud_shift + 16));
		int_buffer.put(pud_int_offset, reg_val);
	}

	@Override
	public boolean gpioRead(int gpio) {
		final int bank = gpio >> 5;
		final int data_shift = gpio % 32;

		return (gpioBanks[bank].get(GPIO_EXT_PORTA) & (1 << data_shift)) != 0;
	}

	@Override
	public void gpioWrite(int gpio, boolean value) {
		final int bank = gpio >> 5;
		final int data_shift = gpio % 32;

		int reg_val = gpioBanks[bank].get(GPIO_SWPORTA_DR);
		if (value) {
			reg_val |= (1 << data_shift);
		} else {
			reg_val &= ~(1 << data_shift);
		}
		gpioBanks[bank].put(GPIO_SWPORTA_DR, reg_val);
	}

	@SuppressWarnings("boxing")
	public static void main(String[] args) {
		int[] gpios = { 0, 39, 40, 47, 124, 157 };

		for (int gpio : gpios) {
			int bank = gpio >> 5;
			int bank_index = gpio % 32;
			int letter = bank_index >> 3;
			int index = bank_index % 8;
			int iomux_int_offset;
			if (bank < 2) {
				iomux_int_offset = IOMUX_INT_OFFSET + bank * 4 + (gpio % 32) / 8;
			} else {
				iomux_int_offset = GRF_GPIO2A_IOMUX + (bank - 2) * 4 + (gpio % 32) / 8;
			}
			System.out.format(
					"GPIO: %d, bank: %d, bank_index: %d, letter: %d, index: %d - GPIO%d_%s%d, iomux int offset: 0x%05x%n",
					gpio, bank, bank_index, letter, index, bank, Character.toString('A' + letter), index,
					iomux_int_offset);
		}

		if (args.length == 0) {
			return;
		}

		int gpio = Integer.parseInt(args[0]);
		int bank = gpio >> 5;
		int bank_index = gpio % 32;
		int letter = bank_index >> 3;
		int index = bank_index % 8;
		System.out.println(
				"Testing with GPIO: " + gpio + ", bank: " + bank + ", bank_index: " + bank_index + ", letter: " + letter
						+ ", index: " + index + " - GPIO" + bank + "_" + Character.toString('A' + letter) + index);

		try (RK3399MmapGpio mmap_gpio = new RK3399MmapGpio()) {
			mmap_gpio.initialise();

			boolean value = false;
			for (int i = 0; i < 20; i++) {
				DeviceMode mode = mmap_gpio.getMode(gpio);
				System.out.println(i + " mode: " + mode);
				if (mode == DeviceMode.DIGITAL_OUTPUT) {
					System.out.println(i + " pre write: " + mmap_gpio.gpioRead(gpio));
					mmap_gpio.gpioWrite(gpio, value);
					System.out.println(i + " post write: " + mmap_gpio.gpioRead(gpio));
					value = !value;
				} else if (mode == DeviceMode.DIGITAL_INPUT) {
					GpioPullUpDown pud = mmap_gpio.getPullUpDown(gpio).get();
					System.out.println(i + " value pre PD change: " + mmap_gpio.gpioRead(gpio) + ", pud: " + pud);
					mmap_gpio.setPullUpDown(gpio, GpioPullUpDown.PULL_DOWN);
					pud = mmap_gpio.getPullUpDown(gpio).get();
					System.out.println(i + " value post PD change: " + mmap_gpio.gpioRead(gpio) + ", pud: " + pud);
					mmap_gpio.setPullUpDown(gpio, GpioPullUpDown.PULL_UP);
					pud = mmap_gpio.getPullUpDown(gpio).get();
					System.out.println(i + " value post PU change: " + mmap_gpio.gpioRead(gpio) + ", pud: " + pud);
					mmap_gpio.setPullUpDown(gpio, GpioPullUpDown.NONE);
					pud = mmap_gpio.getPullUpDown(gpio).get();
					System.out.println(i + " value post NONE change: " + mmap_gpio.gpioRead(gpio) + ", pud: " + pud);
					mmap_gpio.setPullUpDown(gpio, pud);
					pud = mmap_gpio.getPullUpDown(gpio).get();
					System.out.println(i + " value post restoring pud: " + mmap_gpio.gpioRead(gpio) + ", pud: " + pud);
				}
				SleepUtil.sleepSeconds(1);
			}
		} catch (Throwable t) {
			Logger.error(t, "Error: {}", t);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy