178 lines
3.8 KiB
C
178 lines
3.8 KiB
C
|
/* 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, int func)
|
||
|
{
|
||
|
int bit, mode_reg_index, shift;
|
||
|
uint32_t mode_bits, mode_mask;
|
||
|
|
||
|
/* Up to 8 alt functions per port */
|
||
|
if (func > 7)
|
||
|
return;
|
||
|
|
||
|
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);
|
||
|
|