mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 09:54:38 +00:00
226 lines
6.3 KiB
C
226 lines
6.3 KiB
C
|
|
/*
|
||
|
|
* This file is part of Luma3DS
|
||
|
|
* Copyright (C) 2021 Aurora Wright, TuxSH
|
||
|
|
*
|
||
|
|
* 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/>.
|
||
|
|
*
|
||
|
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||
|
|
* * Requiring preservation of specified reasonable legal notices or
|
||
|
|
* author attributions in that material or in the Appropriate Legal
|
||
|
|
* Notices displayed by works containing it.
|
||
|
|
* * Prohibiting misrepresentation of the origin of that material,
|
||
|
|
* or requiring that modified versions of such material be marked in
|
||
|
|
* reasonable ways as different from the original version.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#pragma once
|
||
|
|
|
||
|
|
#include "types.h"
|
||
|
|
|
||
|
|
typedef struct GpioRegisters {
|
||
|
|
u8 gpio1_data;
|
||
|
|
u8 _0x001[0x010 - 0x001];
|
||
|
|
|
||
|
|
u8 gpio2_data;
|
||
|
|
u8 gpio2_dir;
|
||
|
|
u8 gpio2_intcfg;
|
||
|
|
u8 gpio2_inten;
|
||
|
|
u16 gpio2_data2;
|
||
|
|
u8 _0x016[0x020 - 0x016];
|
||
|
|
|
||
|
|
u16 gpio3_data;
|
||
|
|
u16 gpio3_dir;
|
||
|
|
u16 gpio3_intcfg;
|
||
|
|
u16 gpio3_inten;
|
||
|
|
u16 gpio3_data2;
|
||
|
|
u8 _0x02a[0x100 - 0x02A];
|
||
|
|
} GpioRegisters;
|
||
|
|
|
||
|
|
typedef enum GpioDirection {
|
||
|
|
GPIO_DIR_INPUT = 0,
|
||
|
|
GPIO_DIR_OUTPUT = 1,
|
||
|
|
} GpioDirection;
|
||
|
|
|
||
|
|
typedef enum GpioInterruptConfig {
|
||
|
|
GPIO_INTCFG_FALLING_EDGE = 0,
|
||
|
|
GPIO_INTCFG_RISING_EDGE = 1,
|
||
|
|
} GpioInterruptConfig;
|
||
|
|
|
||
|
|
#define GPIO_PIN(bank, idx) (((bank) << 4) | ((idx) & 0xF))
|
||
|
|
#define BANK_OF(pin) ((pin) >> 4)
|
||
|
|
#define BIT_OF(pin) ((pin) & 0xF)
|
||
|
|
|
||
|
|
typedef enum GpioPin {
|
||
|
|
// GPIO1
|
||
|
|
GPIO_DEBUG_BUTTON = GPIO_PIN(1, 0), // active-low
|
||
|
|
GPIO_TOUCH_SCREEN = GPIO_PIN(1, 1), // active-low, 0 when the touch screen is pressed
|
||
|
|
GPIO_SHELL_CLOSED = GPIO_PIN(1, 2),
|
||
|
|
|
||
|
|
// GPIO2
|
||
|
|
GPIO_HEADPHONES_INSERTED = GPIO_PIN(2, 0),
|
||
|
|
GPIO_TWL_DEPOP = GPIO_PIN(2, 1), // active-low
|
||
|
|
|
||
|
|
// GPIO2 (DATA2)
|
||
|
|
GPIO_WIFI_MODE = GPIO_PIN(4, 0), // 0 is CTR, 1 is MP (DS WiFi)
|
||
|
|
|
||
|
|
// GPIO3
|
||
|
|
GPIO_CSTICK_INT = GPIO_PIN(3, 0),
|
||
|
|
GPIO_IRDA_INT = GPIO_PIN(3, 1), // active-low
|
||
|
|
GPIO_GYRO_INT = GPIO_PIN(3, 2),
|
||
|
|
GPIO_CSTICK_STOP = GPIO_PIN(3, 3), // output
|
||
|
|
GPIO_IRDA_TXRC = GPIO_PIN(3, 4), // output
|
||
|
|
GPIO_IRDA_RXD = GPIO_PIN(3, 5), // active-low
|
||
|
|
GPIO_NFC_OUT1 = GPIO_PIN(3, 6), // output
|
||
|
|
GPIO_NFC_OUT2 = GPIO_PIN(3, 7), // output
|
||
|
|
GPIO_HEADPHONES_BUTTON = GPIO_PIN(3, 8), // active-low ("half-inserted")
|
||
|
|
GPIO_MCU_INT = GPIO_PIN(3, 9),
|
||
|
|
GPIO_NFC_INT = GPIO_PIN(3, 10),
|
||
|
|
GPIO_QTM_OUT = GPIO_PIN(3, 11), // output
|
||
|
|
|
||
|
|
// GPIO3 (DATA2)
|
||
|
|
GPIO_WIFI_ENABLED = GPIO_PIN(5, 0),
|
||
|
|
} GpioPin;
|
||
|
|
|
||
|
|
static volatile GpioRegisters *const GPIO = (volatile GpioRegisters *)0x10147000;
|
||
|
|
|
||
|
|
static inline bool gpioRead(GpioPin pin)
|
||
|
|
{
|
||
|
|
u32 bank = BANK_OF(pin);
|
||
|
|
u32 bit = BIT_OF(pin);
|
||
|
|
|
||
|
|
switch (bank) {
|
||
|
|
case 1:
|
||
|
|
return (GPIO->gpio1_data & BIT(bit)) != 0;
|
||
|
|
case 2:
|
||
|
|
return (GPIO->gpio2_data & BIT(bit)) != 0;
|
||
|
|
case 3:
|
||
|
|
return (GPIO->gpio3_data & BIT(bit)) != 0;
|
||
|
|
case 4:
|
||
|
|
return (GPIO->gpio2_data2 & BIT(bit)) != 0;
|
||
|
|
case 5:
|
||
|
|
return (GPIO->gpio3_data2 & BIT(bit)) != 0;
|
||
|
|
|
||
|
|
default:
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void gpioWrite(GpioPin pin, bool val)
|
||
|
|
{
|
||
|
|
u32 bank = BANK_OF(pin);
|
||
|
|
u32 bit = BIT_OF(pin);
|
||
|
|
|
||
|
|
u32 valMask = (val ? 1 : 0) << bit;
|
||
|
|
u32 tmp;
|
||
|
|
|
||
|
|
switch (bank) {
|
||
|
|
case 1:
|
||
|
|
tmp = GPIO->gpio1_data & ~BIT(bit);
|
||
|
|
GPIO->gpio1_data = (u8)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
tmp = GPIO->gpio2_data & ~BIT(bit);
|
||
|
|
GPIO->gpio2_data = (u8)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
tmp = GPIO->gpio3_data & ~BIT(bit);
|
||
|
|
GPIO->gpio3_data = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
tmp = GPIO->gpio2_data2 & ~BIT(bit);
|
||
|
|
GPIO->gpio2_data2 = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
tmp = GPIO->gpio3_data2 & ~BIT(bit);
|
||
|
|
GPIO->gpio3_data2 = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void gpioSetDirection(GpioPin pin, GpioDirection direction)
|
||
|
|
{
|
||
|
|
u32 bank = BANK_OF(pin);
|
||
|
|
u32 bit = BIT_OF(pin);
|
||
|
|
|
||
|
|
u32 valMask = (direction == GPIO_DIR_OUTPUT ? 1 : 0) << bit;
|
||
|
|
u32 tmp;
|
||
|
|
|
||
|
|
switch (bank) {
|
||
|
|
case 2:
|
||
|
|
tmp = GPIO->gpio2_dir & ~BIT(bit);
|
||
|
|
GPIO->gpio2_dir = (u8)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
tmp = GPIO->gpio3_dir & ~BIT(bit);
|
||
|
|
GPIO->gpio3_dir = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void gpioConfigureInterrupt(GpioPin pin, GpioInterruptConfig cfg)
|
||
|
|
{
|
||
|
|
u32 bank = BANK_OF(pin);
|
||
|
|
u32 bit = BIT_OF(pin);
|
||
|
|
|
||
|
|
u32 valMask = (cfg == GPIO_INTCFG_RISING_EDGE ? 1 : 0) << bit;
|
||
|
|
u32 tmp;
|
||
|
|
|
||
|
|
switch (bank) {
|
||
|
|
case 2:
|
||
|
|
tmp = GPIO->gpio2_intcfg & ~BIT(bit);
|
||
|
|
GPIO->gpio2_intcfg = (u8)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
tmp = GPIO->gpio3_data & ~BIT(bit);
|
||
|
|
GPIO->gpio3_intcfg = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void gpioSetInterruptEnabled(GpioPin pin, bool enabled)
|
||
|
|
{
|
||
|
|
u32 bank = BANK_OF(pin);
|
||
|
|
u32 bit = BIT_OF(pin);
|
||
|
|
|
||
|
|
u32 valMask = (enabled ? 1 : 0) << bit;
|
||
|
|
u32 tmp;
|
||
|
|
|
||
|
|
switch (bank) {
|
||
|
|
case 2:
|
||
|
|
tmp = GPIO->gpio2_inten & ~BIT(bit);
|
||
|
|
GPIO->gpio2_inten = (u8)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
tmp = GPIO->gpio3_inten & ~BIT(bit);
|
||
|
|
GPIO->gpio3_inten = (u16)(tmp | valMask);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#undef GPIO_PIN
|
||
|
|
#undef BIT_OF
|
||
|
|
#undef BANK_OF
|