coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/nrf51/gpio.c

309 lines
7.3 KiB
C
Raw Permalink Normal View History

2024-03-04 11:14:53 +01:00
/* Copyright 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "common.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "task.h"
#include "util.h"
/*
* For each interrupt (INT0-INT3, PORT), record which GPIO entry uses it.
*/
static const struct gpio_info *gpio_ints[NRF51_GPIOTE_IN_COUNT];
static const struct gpio_info *gpio_int_port;
volatile uint32_t * const nrf51_alt_funcs[] = {
/* UART */
&NRF51_UART_PSELRTS,
&NRF51_UART_PSELTXD,
&NRF51_UART_PSELCTS,
&NRF51_UART_PSELRXD,
/* SPI1 (SPI Master) */
&NRF51_SPI0_PSELSCK,
&NRF51_SPI0_PSELMOSI,
&NRF51_SPI0_PSELMISO,
/* TWI0 (I2C) */
&NRF51_TWI0_PSELSCL,
&NRF51_TWI0_PSELSDA,
/* SPI1 (SPI Master) */
&NRF51_SPI1_PSELSCK,
&NRF51_SPI1_PSELMOSI,
&NRF51_SPI1_PSELMISO,
/* TWI1 (I2C) */
&NRF51_TWI1_PSELSCL,
&NRF51_TWI1_PSELSDA,
/* SPIS1 (SPI SLAVE) */
&NRF51_SPIS1_PSELSCK,
&NRF51_SPIS1_PSELMISO,
&NRF51_SPIS1_PSELMOSI,
&NRF51_SPIS1_PSELCSN,
/* QDEC (ROTARY DECODER) */
&NRF51_QDEC_PSELLED,
&NRF51_QDEC_PSELA,
&NRF51_QDEC_PSELB,
/* LPCOMP (Low Power Comparator) */
&NRF51_LPCOMP_PSEL,
};
const unsigned int nrf51_alt_func_count = ARRAY_SIZE(nrf51_alt_funcs);
/* Make sure the function table and defines stay in sync */
BUILD_ASSERT(NRF51_MAX_ALT_FUNCS == ARRAY_SIZE(nrf51_alt_funcs));
void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
{
uint32_t val = 0;
uint32_t bit = GPIO_MASK_TO_NUM(mask);
if (flags & GPIO_OUTPUT)
val |= NRF51_PIN_CNF_DIR_OUTPUT;
else if (flags & GPIO_INPUT)
val |= NRF51_PIN_CNF_DIR_INPUT;
if (flags & GPIO_PULL_DOWN)
val |= NRF51_PIN_CNF_PULLDOWN;
else if (flags & GPIO_PULL_UP)
val |= NRF51_PIN_CNF_PULLUP;
/* TODO: Drive strength? H0D1? */
if (flags & GPIO_OPEN_DRAIN)
val |= NRF51_PIN_CNF_DRIVE_S0D1;
if (flags & GPIO_OUTPUT) {
if (flags & GPIO_HIGH)
NRF51_GPIO0_OUTSET = mask;
else if (flags & GPIO_LOW)
NRF51_GPIO0_OUTCLR = mask;
}
/* Interrupt levels */
if (flags & GPIO_INT_SHARED) {
/*
* There are no shared edge-triggered interrupts;
* they're either high or low.
*/
ASSERT((flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) == 0);
ASSERT((flags & GPIO_INT_LEVEL) != GPIO_INT_LEVEL);
if (flags & GPIO_INT_F_LOW)
val |= NRF51_PIN_CNF_SENSE_LOW;
else if (flags & GPIO_INT_F_HIGH)
val |= NRF51_PIN_CNF_SENSE_HIGH;
}
NRF51_PIN_CNF(bit) = val;
}
static void gpio_init(void)
{
task_enable_irq(NRF51_PERID_GPIOTE);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
test_mockable int gpio_get_level(enum gpio_signal signal)
{
return !!(NRF51_GPIO0_IN & gpio_list[signal].mask);
}
void gpio_set_level(enum gpio_signal signal, int value)
{
if (value)
NRF51_GPIO0_OUTSET = gpio_list[signal].mask;
else
NRF51_GPIO0_OUTCLR = gpio_list[signal].mask;
}
void gpio_pre_init(void)
{
const struct gpio_info *g = gpio_list;
int is_warm = 0;
int i;
if (NRF51_POWER_RESETREAS &
(NRF51_POWER_RESETREAS_OFF | /* GPIO Wake */
NRF51_POWER_RESETREAS_LPCOMP)) {
/* This is a warm reboot */
is_warm = 1;
}
/* Initialize Interrupt configuration */
for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++)
gpio_ints[i] = NULL;
gpio_int_port = NULL;
/* Set all GPIOs to defaults */
for (i = 0; i < GPIO_COUNT; i++, g++) {
int flags = g->flags;
if (flags & GPIO_DEFAULT)
continue;
/*
* If this is a warm reboot, don't set the output levels again.
*/
if (is_warm)
flags &= ~(GPIO_LOW | GPIO_HIGH);
/* Set up GPIO based on flags */
gpio_set_flags_by_mask(g->port, g->mask, flags);
}
}
/*
* NRF51 doesn't have an alternate function table.
* Use the pin select registers in place of the function number.
*/
void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
{
uint32_t bit = GPIO_MASK_TO_NUM(mask);
ASSERT((~mask & BIT(bit)) == 0); /* Only one bit set. */
ASSERT(port == GPIO_0);
ASSERT((func >= 0 && func < nrf51_alt_func_count) || func == -1);
/* Remove the previous setting(s) */
if (func == -1) {
int i;
for (i = 0; i < nrf51_alt_func_count; i++) {
if (*(nrf51_alt_funcs[i]) == bit)
*(nrf51_alt_funcs[i]) = 0xffffffff;
}
} else {
*(nrf51_alt_funcs[func]) = bit;
}
}
/*
* Enable the interrupt associated with the "signal"
* The architecture has one general (PORT)
* and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts.
*
*/
int gpio_enable_interrupt(enum gpio_signal signal)
{
int pin;
const struct gpio_info *g = gpio_list + signal;
/* Fail if not implemented or no interrupt handler */
if (!g->mask || signal >= GPIO_IH_COUNT)
return EC_ERROR_INVAL;
/* If it's not shared, use INT0-INT3, otherwise use PORT. */
if (!(g->flags & GPIO_INT_SHARED)) {
int int_num, free_slot = -1;
uint32_t event_config = 0;
for (int_num = 0; int_num < NRF51_GPIOTE_IN_COUNT; int_num++) {
if (gpio_ints[int_num] == g)
return EC_SUCCESS; /* This is already set up. */
if (gpio_ints[int_num] == NULL && free_slot == -1)
free_slot = int_num;
}
ASSERT(free_slot != -1);
gpio_ints[free_slot] = g;
pin = GPIO_MASK_TO_NUM(g->mask);
event_config = (pin << NRF51_GPIOTE_PSEL_POS) |
NRF51_GPIOTE_MODE_EVENT;
ASSERT(g->flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING));
/* RISING | FALLING = TOGGLE */
if (g->flags & GPIO_INT_F_RISING)
event_config |= NRF51_GPIOTE_POLARITY_LOTOHI;
if (g->flags & GPIO_INT_F_FALLING)
event_config |= NRF51_GPIOTE_POLARITY_HITOLO;
NRF51_GPIOTE_CONFIG(free_slot) = event_config;
/* Enable the IN[] interrupt. */
NRF51_GPIOTE_INTENSET = 1 << free_slot;
} else {
/* The first handler for the shared interrupt wins. */
if (gpio_int_port == NULL) {
gpio_int_port = g;
/* Enable the PORT interrupt. */
NRF51_GPIOTE_INTENSET = 1 << NRF51_GPIOTE_PORT_BIT;
}
}
return EC_SUCCESS;
}
/*
* Disable the interrupt associated with the "signal"
* The architecture has one general (PORT)
* and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts.
*/
int gpio_disable_interrupt(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
int i;
/* Fail if not implemented or no interrupt handler */
if (!g->mask || signal >= GPIO_IH_COUNT)
return EC_ERROR_INVAL;
/* If it's not shared, use INT0-INT3, otherwise use PORT. */
if (!(g->flags & GPIO_INT_SHARED)) {
for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) {
/* Remove matching handler. */
if (gpio_ints[i] == g) {
/* Disable the interrupt */
NRF51_GPIOTE_INTENCLR =
1 << NRF51_GPIOTE_IN_BIT(i);
/* Zero the handler */
gpio_ints[i] = NULL;
}
}
} else {
/* Disable the interrupt */
NRF51_GPIOTE_INTENCLR = 1 << NRF51_GPIOTE_PORT_BIT;
/* Zero the shared handler */
gpio_int_port = NULL;
}
return EC_SUCCESS;
}
/*
* Clear interrupt and run handler.
*/
void gpio_interrupt(void)
{
const struct gpio_info *g;
int i;
int signal;
for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) {
if (NRF51_GPIOTE_IN(i)) {
NRF51_GPIOTE_IN(i) = 0;
g = gpio_ints[i];
signal = g - gpio_list;
if (g && signal < GPIO_IH_COUNT)
gpio_irq_handlers[signal](signal);
}
}
if (NRF51_GPIOTE_PORT) {
NRF51_GPIOTE_PORT = 0;
g = gpio_int_port;
signal = g - gpio_list;
if (g && signal < GPIO_IH_COUNT)
gpio_irq_handlers[signal](signal);
}
}
DECLARE_IRQ(NRF51_PERID_GPIOTE, gpio_interrupt, 1);