blob: cd8a6633063157278ef00e953ab6c4a85e5c5bd2 [file] [log] [blame]
/*
* Copyright (C) 2021, HENSOLDT Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <stdint.h>
#include <utils/util.h>
#include <platsupport/gpio.h>
#include <platsupport/plat/gpio.h>
#include "../../services.h"
#define BCM2837_GPIO_PADDR 0x3f200000
#define BCM2837_GPIO_SIZE 0x1000
// Registers according to section 6.1 of BCM2835 TRM
typedef volatile struct bcm2837_gpio_regs {
uint32_t gpfsel0; /* +0x00 */
uint32_t gpfsel1; /* +0x04 */
uint32_t gpfsel2; /* +0x08 */
uint32_t gpfsel3; /* +0x0c */
uint32_t gpfsel4; /* +0x10 */
uint32_t gpfsel5; /* +0x14 */
uint32_t unused0;
uint32_t gpset0; /* +0x1c */
uint32_t gpset1; /* +0x20 */
uint32_t unused1;
uint32_t gpclr0; /* +0x28 */
uint32_t gpclr1; /* +0x2c */
uint32_t unused2;
uint32_t gplev0; /* +0x34 */
uint32_t gplev1; /* +0x38 */
uint32_t unused3;
uint32_t gpeds0; /* +0x40 */
uint32_t gpeds1; /* +0x44 */
uint32_t unused4;
uint32_t gpren0; /* +0x4c */
uint32_t gpren1; /* +0x50 */
uint32_t unused5;
uint32_t gpfen0; /* +0x58 */
uint32_t gpfen1; /* +0x5c */
uint32_t unused6;
uint32_t gphen0; /* +0x64 */
uint32_t gphen1; /* +0x68 */
uint32_t unused7;
uint32_t gplen0; /* +0x70 */
uint32_t gplen1; /* +0x74 */
uint32_t unused8;
uint32_t gparen0; /* +0x7c */
uint32_t gparen1; /* +0x80 */
uint32_t unused9;
uint32_t gpafen0; /* +0x88 */
uint32_t gpafen1; /* +0x8c */
uint32_t unused10;
uint32_t gppud; /* +0x94 */
uint32_t gppudclk0; /* +0x98 */
uint32_t gppudclk1; /* +0x9c */
} bcm2837_gpio_regs_t;
static struct bcm2837_gpio {
bcm2837_gpio_regs_t *bank[GPIO_NBANKS];
} gpio_ctx;
static bcm2837_gpio_regs_t *bcm2837_gpio_get_bank(gpio_t *gpio)
{
assert(gpio);
assert(gpio->gpio_sys);
assert(gpio->gpio_sys->priv);
struct bcm2837_gpio *gpio_priv = (struct bcm2837_gpio *)gpio->gpio_sys->priv;
int port = 0;
return gpio_priv->bank[port];
}
static int bcm2837_gpio_init(gpio_sys_t *gpio_sys, int id, enum gpio_dir dir, gpio_t *gpio)
{
assert(gpio);
assert(gpio_sys);
struct bcm2837_gpio *gpio_priv = (struct bcm2837_gpio *)gpio_sys->priv;
assert(gpio_priv);
if (id < 0 || id > MAX_GPIO_ID) {
ZF_LOGE("GPIO Pin ID is not in valid range!");
return -1;
}
gpio->id = id;
gpio->gpio_sys = gpio_sys;
return 0;
}
/*
* GPIO set level function
*
* See BCM2835 TRM, section 6 General Purpose I/O (GPIO):
* The bcm2837 SoC features 54 GPIO pins. The level of each of these pins can
* be set with the registers gpset0 (0x1c), gpset1 (0x20) and can be cleared
* with the registers gpclr0 (0x28), gpclr1 (0x2c). Each bit in one of these
* GPIO registers is responsible for setting/clearing the level of a GPIO pin.
* Separating the set and clear functions removes the need for read-modify-write
* operations.
*
* A prerequisite for using this function is to configure the respective GPIO
* pin to OUT (with the bcm2837_gpio_fsel function).
*/
static int bcm2837_gpio_set_level(gpio_t *gpio, enum gpio_level level)
{
bcm2837_gpio_regs_t *bank = bcm2837_gpio_get_bank(gpio);
assert(bank);
int pin = gpio->id;
if (pin < 0 || pin > MAX_GPIO_ID) {
ZF_LOGE("GPIO Pin ID is not in valid range!");
return -1;
}
volatile uint32_t *paddr = (level == GPIO_LEVEL_HIGH) ? &(bank->gpset0) + (pin / 32)
: &(bank->gpclr0) + (pin / 32);
uint8_t shift = (pin % 32);
*paddr = (1u << shift);
return 0;
}
/*
* GPIO read level function
*
* See BCM2835 TRM, section 6 General Purpose I/O (GPIO):
* The bcm2837 SoC features 54 GPIO pins. The level of each of these pins can
* be read with the registers gplev0 (0x34) and gplev1 (0x38). Each bit in one
* of these GPIO registers is responsible for reading the level of a GPIO pin.
* Separating the set and clear functions removes the need for read-modify-write
* operations.
*
* A prerequisite for using this function is to configure the respective GPIO
* pin to OUT (with the bcm2837_gpio_fsel function).
*/
static int bcm2837_gpio_read_level(gpio_t *gpio)
{
bcm2837_gpio_regs_t *bank = bcm2837_gpio_get_bank(gpio);
assert(bank);
int pin = gpio->id;
if (pin < 0 || pin > MAX_GPIO_ID) {
ZF_LOGE("GPIO Pin ID is not in valid range!");
return -1;
}
volatile uint32_t *paddr = &(bank->gplev0) + (pin / 32);
uint8_t shift = pin % 32;
uint32_t value = *paddr;
return (value & (1u << shift)) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
}
/*
* GPIO function select
*
* See BCM2835 TRM, section 6.1 Register View:
* The bcm2837 has 6 GPIO function select registers. Each of these control
* registers controls 10 GPIO pins of a total of 54 GPIO pins. As a result each
* of these control registers has 10 sets of 3 bits per GPIO pin. Depending on
* the mode given, these 3 bits are set accordingly for a specific pin i:
*
* 000 = GPIO Pin i is an input
* 001 = GPIO Pin i is an output
* 100 = GPIO Pin i takes alternate function 0
* 101 = GPIO Pin i takes alternate function 1
* 110 = GPIO Pin i takes alternate function 2
* 111 = GPIO Pin i takes alternate function 3
* 011 = GPIO Pin i takes alternate function 4
* 010 = GPIO Pin i takes alternate function 5
*
* To calculate the 3 bits for Pin i use the following formula:
* i / 10 + ((i % 10) * 3)
*/
int bcm2837_gpio_fsel(gpio_t *gpio, uint8_t mode)
{
bcm2837_gpio_regs_t *bank = bcm2837_gpio_get_bank(gpio);
assert(bank);
int pin = gpio->id;
if (pin < 0 || pin > MAX_GPIO_ID) {
ZF_LOGE("GPIO Pin ID is not in valid range!");
return -1;
}
volatile uint32_t *paddr = &(bank->gpfsel0) + (pin / 10);
uint8_t shift = (pin % 10) * 3;
uint32_t mask = BCM2837_GPIO_FSEL_MASK << shift;
uint32_t value = mode << shift;
uint32_t v = *paddr;
v = (v & ~mask) | (value & mask);
*paddr = v;
return 0;
}
int bcm2837_gpio_init_common(gpio_sys_t *gpio_sys)
{
gpio_sys->priv = (void *)&gpio_ctx;
gpio_sys->set_level = &bcm2837_gpio_set_level;
gpio_sys->read_level = &bcm2837_gpio_read_level;
gpio_sys->init = &bcm2837_gpio_init;
return 0;
}
int bcm2837_gpio_sys_init(void *bank1, gpio_sys_t *gpio_sys)
{
if (bank1 != NULL) {
gpio_ctx.bank[GPIO_BANK1] = bank1;
}
return bcm2837_gpio_init_common(gpio_sys);
}
int gpio_sys_init(ps_io_ops_t *io_ops, gpio_sys_t *gpio_sys)
{
MAP_IF_NULL(io_ops, BCM2837_GPIO, gpio_ctx.bank[0]);
return bcm2837_gpio_init_common(gpio_sys);
}