mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:27:45 +00:00
Kernel: Add a GPIO class for aarch64
This allows configuring the alternate pin functions and pin pull up/down states, which is needed for using the UART.
This commit is contained in:
parent
5ac82efbe1
commit
fdc86ddae5
6 changed files with 187 additions and 2 deletions
98
Kernel/Prekernel/Arch/aarch64/GPIO.cpp
Normal file
98
Kernel/Prekernel/Arch/aarch64/GPIO.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Nico Weber <thakis@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Prekernel/Arch/aarch64/GPIO.h>
|
||||
#include <Kernel/Prekernel/Arch/aarch64/MMIO.h>
|
||||
|
||||
extern "C" void wait_cycles(int n);
|
||||
|
||||
namespace Prekernel {
|
||||
|
||||
// See BCM2835-ARM-Peripherals.pdf section "6 General Purpose I/O" or bcm2711-peripherals.pdf "Chapter 5. General Purpose I/O".
|
||||
|
||||
// "6.1 Register View" / "5.2 Register View"
|
||||
|
||||
struct PinData {
|
||||
u32 bits[2];
|
||||
u32 reserved;
|
||||
};
|
||||
|
||||
struct GPIOControlRegisters {
|
||||
u32 function_select[6]; // Every u32 stores a 3-bit function code for 10 pins.
|
||||
u32 reserved;
|
||||
PinData output_set;
|
||||
PinData output_clear;
|
||||
PinData level;
|
||||
PinData event_detect_status;
|
||||
PinData rising_edge_detect_enable;
|
||||
PinData falling_edge_detect_enable;
|
||||
PinData high_detect_enable;
|
||||
PinData low_detect_enable;
|
||||
PinData async_rising_edge_detect_enable;
|
||||
PinData async_falling_edge_detect_enable;
|
||||
u32 pull_up_down_enable;
|
||||
PinData pull_up_down_enable_clock;
|
||||
u32 test;
|
||||
};
|
||||
|
||||
GPIO::GPIO()
|
||||
: m_registers(MMIO::the().peripheral<GPIOControlRegisters>(0x20'0000))
|
||||
{
|
||||
}
|
||||
|
||||
GPIO& GPIO::the()
|
||||
{
|
||||
static GPIO instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void GPIO::set_pin_function(unsigned pin_number, PinFunction function)
|
||||
{
|
||||
// pin_number must be <= 53. We can't VERIFY() that since this function runs too early to print assertion failures.
|
||||
|
||||
unsigned function_select_index = pin_number / 10;
|
||||
unsigned function_select_bits_start = (pin_number % 10) * 3;
|
||||
|
||||
u32 function_bits = m_registers->function_select[function_select_index];
|
||||
function_bits = (function_bits & ~(0b111 << function_select_bits_start)) | (static_cast<u32>(function) << function_select_bits_start);
|
||||
m_registers->function_select[function_select_index] = function_bits;
|
||||
}
|
||||
|
||||
void GPIO::internal_enable_pins(u32 enable[2], PullUpDownState state)
|
||||
{
|
||||
// Section "GPIO Pull-up/down Clock Registers (GPPUDCLKn)":
|
||||
// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on
|
||||
// the respective GPIO pins. These registers must be used in conjunction with the GPPUD
|
||||
// register to effect GPIO Pull-up/down changes. The following sequence of events is
|
||||
// required:
|
||||
// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither
|
||||
// to remove the current Pull-up/down)
|
||||
m_registers->pull_up_down_enable = static_cast<u32>(state);
|
||||
|
||||
// 2. Wait 150 cycles – this provides the required set-up time for the control signal
|
||||
wait_cycles(150);
|
||||
|
||||
// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to
|
||||
// modify – NOTE only the pads which receive a clock will be modified, all others will
|
||||
// retain their previous state.
|
||||
m_registers->pull_up_down_enable_clock.bits[0] = enable[0];
|
||||
m_registers->pull_up_down_enable_clock.bits[1] = enable[1];
|
||||
|
||||
// 4. Wait 150 cycles – this provides the required hold time for the control signal
|
||||
wait_cycles(150);
|
||||
|
||||
// 5. Write to GPPUD to remove the control signal
|
||||
m_registers->pull_up_down_enable = 0;
|
||||
|
||||
// 6. Write to GPPUDCLK0/1 to remove the clock
|
||||
m_registers->pull_up_down_enable_clock.bits[0] = 0;
|
||||
m_registers->pull_up_down_enable_clock.bits[1] = 0;
|
||||
|
||||
// bcm2711-peripherals.pdf documents GPIO_PUP_PDN_CNTRL_REG[4] registers that store 2 bits state per register, similar to function_select.
|
||||
// I don't know if the RPi3 has that already, so this uses the old BCM2835 approach for now.
|
||||
}
|
||||
|
||||
}
|
60
Kernel/Prekernel/Arch/aarch64/GPIO.h
Normal file
60
Kernel/Prekernel/Arch/aarch64/GPIO.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Nico Weber <thakis@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Prekernel {
|
||||
|
||||
struct GPIOControlRegisters;
|
||||
|
||||
// Can configure the general-purpose I/O registers on a Raspberry Pi.
|
||||
class GPIO {
|
||||
public:
|
||||
enum class PinFunction {
|
||||
Input = 0,
|
||||
Output = 1,
|
||||
Alternate0 = 0b100,
|
||||
Alternate1 = 0b101,
|
||||
Alternate2 = 0b110,
|
||||
Alternate3 = 0b111,
|
||||
Alternate4 = 0b011,
|
||||
Alternate5 = 0b010,
|
||||
};
|
||||
|
||||
static GPIO& the();
|
||||
|
||||
void set_pin_function(unsigned pin_number, PinFunction);
|
||||
|
||||
enum class PullUpDownState {
|
||||
Disable = 0,
|
||||
PullDown = 1,
|
||||
PullUp = 2,
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
void set_pin_pull_up_down_state(Array<int, N> pins, PullUpDownState state)
|
||||
{
|
||||
u32 enable[2] = {};
|
||||
for (int pin : pins) {
|
||||
if (pin < 32)
|
||||
enable[0] |= (1 << pin);
|
||||
else
|
||||
enable[1] |= (1 << (pin - 32));
|
||||
}
|
||||
internal_enable_pins(enable, state);
|
||||
}
|
||||
|
||||
private:
|
||||
GPIO();
|
||||
void internal_enable_pins(u32 enable[2], PullUpDownState state);
|
||||
|
||||
GPIOControlRegisters volatile* m_registers;
|
||||
};
|
||||
|
||||
}
|
|
@ -18,8 +18,12 @@ class MMIO {
|
|||
public:
|
||||
static MMIO& the();
|
||||
|
||||
u32 read(FlatPtr offset) { return *(u32 volatile*)(m_base_address + offset); }
|
||||
void write(FlatPtr offset, u32 value) { *(u32 volatile*)(m_base_address + offset) = value; }
|
||||
u32 read(FlatPtr offset) { return *peripheral_address(offset); }
|
||||
void write(FlatPtr offset, u32 value) { *peripheral_address(offset) = value; }
|
||||
|
||||
u32 volatile* peripheral_address(FlatPtr offset) { return (u32 volatile*)(m_base_address + offset); }
|
||||
template<class T>
|
||||
T volatile* peripheral(FlatPtr offset) { return (T volatile*)peripheral_address(offset); }
|
||||
|
||||
private:
|
||||
MMIO();
|
||||
|
|
|
@ -21,3 +21,13 @@ start:
|
|||
mov sp, x14
|
||||
|
||||
b init
|
||||
|
||||
.globl wait_cycles
|
||||
.type wait_cycles, @function
|
||||
wait_cycles:
|
||||
Lstart:
|
||||
// This is probably too fast when caching and branch prediction is turned on.
|
||||
// FIXME: Make timer-based.
|
||||
subs x0, x0, #1
|
||||
bne Lstart
|
||||
ret
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Prekernel/Arch/aarch64/GPIO.h>
|
||||
#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
|
||||
|
||||
extern "C" [[noreturn]] void halt();
|
||||
|
@ -12,6 +13,12 @@ extern "C" [[noreturn]] void halt();
|
|||
extern "C" [[noreturn]] void init();
|
||||
extern "C" [[noreturn]] void init()
|
||||
{
|
||||
auto& gpio = Prekernel::GPIO::the();
|
||||
gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0);
|
||||
gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0);
|
||||
|
||||
gpio.set_pin_pull_up_down_state(Array { 14, 15 }, Prekernel::GPIO::PullUpDownState::Disable);
|
||||
|
||||
[[maybe_unused]] u32 firmware_version = Prekernel::Mailbox::query_firmware_version();
|
||||
halt();
|
||||
}
|
||||
|
@ -32,3 +39,8 @@ void __stack_chk_fail()
|
|||
{
|
||||
halt();
|
||||
}
|
||||
|
||||
[[noreturn]] void __assertion_failed(char const*, char const*, unsigned int, char const*)
|
||||
{
|
||||
halt();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64")
|
|||
Arch/aarch64/boot.S
|
||||
|
||||
${SOURCES}
|
||||
Arch/aarch64/GPIO.cpp
|
||||
Arch/aarch64/Mailbox.cpp
|
||||
Arch/aarch64/MainIdRegister.cpp
|
||||
Arch/aarch64/MMIO.cpp
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue