blob: a4896aae721f5f4e9f856e986a47d1a62e692cdb [file] [log] [blame]
/* Copyright 2018 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.
*/
/* GPIO module */
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "util.h"
void gpio_set_alternate_function(uint32_t port, uint32_t mask,
enum gpio_alternate_func func)
{
int bit, mode_reg_index, shift;
uint32_t mode_bits, mode_mask;
/* Up to 8 alt functions per port */
if (func > GPIO_ALT_FUNC_7)
return;
if (func == GPIO_ALT_FUNC_NONE)
func = GPIO_ALT_FUNC_DEFAULT;
while (mask) {
/* 32 gpio per port */
bit = get_next_bit(&mask);
/* 8 gpio per mode reg */
mode_reg_index = (port << 2) | (bit >> 3);
/*
* b[3] - write enable(?)
* b[2:0] - mode
*/
shift = (bit & 7) << 2;
mode_bits = func << shift;
mode_mask = ~(0xf << shift);
AP_GPIO_MODE(mode_reg_index) = (AP_GPIO_MODE(mode_reg_index) &
mode_mask) | mode_bits;
}
}
test_mockable int gpio_get_level(enum gpio_signal signal)
{
return !!(AP_GPIO_DIN(gpio_list[signal].port) &
gpio_list[signal].mask);
}
void gpio_set_level(enum gpio_signal signal, int value)
{
if (value)
AP_GPIO_DOUT(gpio_list[signal].port) |= gpio_list[signal].mask;
else
AP_GPIO_DOUT(gpio_list[signal].port) &= ~gpio_list[signal].mask;
}
void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
{
/* Set input/output mode */
if (flags & GPIO_OUTPUT) {
/* Set level before changing to output mode */
if (flags & GPIO_HIGH)
AP_GPIO_DOUT(port) |= mask;
if (flags & GPIO_LOW)
AP_GPIO_DOUT(port) &= ~mask;
AP_GPIO_DIR(port) |= mask;
} else {
AP_GPIO_DIR(port) &= ~mask;
}
if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_HIGH))
SCP_EINT_POLARITY_SET[port] = mask;
if (flags & (GPIO_INT_F_FALLING | GPIO_INT_F_LOW))
SCP_EINT_POLARITY_CLR[port] = mask;
else
SCP_EINT_POLARITY_SET[port] = mask;
/* Set sensitivity register on edge trigger */
if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING))
SCP_EINT_SENS_SET[port] = mask;
else
SCP_EINT_SENS_CLR[port] = mask;
}
int gpio_get_flags_by_mask(uint32_t port, uint32_t mask)
{
/* TODO(b/120167145): implement get flags */
return 0;
}
int gpio_enable_interrupt(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
if (signal >= GPIO_IH_COUNT || !g->mask)
return EC_ERROR_INVAL;
SCP_EINT_MASK_CLR[g->port] = g->mask;
return EC_SUCCESS;
}
int gpio_disable_interrupt(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
if (signal >= GPIO_IH_COUNT || !g->mask)
return EC_ERROR_INVAL;
SCP_EINT_MASK_SET[g->port] = g->mask;
return EC_SUCCESS;
}
int gpio_clear_pending_interrupt(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
if (signal >= GPIO_IH_COUNT || !g->mask)
return EC_ERROR_INVAL;
SCP_EINT_ACK[g->port] = g->mask;
return EC_SUCCESS;
}
void gpio_pre_init(void)
{
const struct gpio_info *g = gpio_list;
int i;
int is_warm = system_is_reboot_warm();
for (i = 0; i < GPIO_COUNT; i++, g++) {
int flags = g->flags;
if (flags & GPIO_DEFAULT)
continue;
if (is_warm)
flags &= ~(GPIO_LOW | GPIO_HIGH);
gpio_set_flags_by_mask(g->port, g->mask, flags);
}
}
void gpio_init(void)
{
/* Enable EINT IRQ */
task_enable_irq(SCP_IRQ_EINT);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
/* Interrupt handler */
void __keep gpio_interrupt(void)
{
int bit, port;
uint32_t pending;
enum gpio_signal signal;
for (port = 0; port <= MAX_EINT_PORT; port++) {
pending = SCP_EINT_STATUS[port];
while (pending) {
bit = get_next_bit(&pending);
SCP_EINT_ACK[port] = BIT(bit);
/* Skip masked gpio */
if (SCP_EINT_MASK_GET[port] & BIT(bit))
continue;
/* Call handler */
signal = port * 32 + bit;
if (signal < GPIO_IH_COUNT)
gpio_irq_handlers[signal](signal);
}
}
}
DECLARE_IRQ(SCP_IRQ_EINT, gpio_interrupt, 1);