| /* | |
| * 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); | |
| } |