1
Fork 0
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:
Nico Weber 2021-09-25 17:53:42 -04:00 committed by Linus Groh
parent 5ac82efbe1
commit fdc86ddae5
6 changed files with 187 additions and 2 deletions

View 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.
}
}

View 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;
};
}

View file

@ -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();

View file

@ -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

View file

@ -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();
}

View file

@ -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