Commit c084a687 authored by Florian Kargl's avatar Florian Kargl Committed by Rahix
Browse files

refactor(portexpander): Adjust portexpander API to match GPIO

- Add portexpander interrupt handling
- Prevent redundant interrupts between polling portexpander
parent 0ba05918
......@@ -26,7 +26,7 @@ uint8_t epic_buttons_read(uint8_t mask)
* Not using PB_Get() here as that performs one I2C transcation
* per button.
*/
uint8_t pin_status = ~portexpander_get();
uint8_t pin_status = ~portexpander_in_get(0xFF);
hwlock_release(HWLOCK_I2C);
......
......@@ -215,6 +215,7 @@ void core1_stop(void)
void card10_poll(void)
{
pmic_poll();
portexpander_poll();
}
void card10_reset(void)
......
......@@ -22,7 +22,7 @@ void display_set_reset_pin(uint8_t state)
if (!portexpander_detected()) {
MAX77650_setDO(state ? true : false);
} else {
portexpander_set(4, state);
portexpander_out_put(PIN_4, state);
}
}
......
......@@ -116,7 +116,7 @@ int PB_Get(unsigned int pb)
case 3:
case 4:
if (portexpander_detected()) {
uint8_t port = portexpander_get();
uint8_t port = portexpander_in_get(0xFF);
return (port & (1 << expander_pins[pb - 1])) == 0;
} else {
return GPIO_InGet(&pb_pin[pb - 1]) == 0;
......
/* PCAL6408A I2C port expander */
/* **** Includes **** */
#include "portexpander.h"
#include "mxc_config.h"
#include "mxc_assert.h"
#include "i2c.h"
#include <stdio.h>
......@@ -7,10 +12,7 @@
#include <string.h>
#include <stdbool.h>
// PCAL6408A I2C port expander
static bool detected = false;
static uint8_t output_state;
/* **** Definitions **** */
/* clang-format off */
#define PE_ADDR 0x42
......@@ -40,54 +42,147 @@ static uint8_t output_state;
#define PE_INPUT_MASK ((uint8_t)0b01101000) // 3, 5, 6 = input
/* **** Globals **** */
static bool detected = false;
static volatile bool interrupt_pending;
static uint8_t type_state = 0xFF;
static uint8_t output_state = 0xFF;
static uint8_t pull_enable_state = 0x00;
static uint8_t pull_selection_state = 0xFF;
static uint8_t int_mask_state = 0xFF;
static gpio_int_pol_t int_edge_config[8] = { 0 };
static pe_callback callbacks[8] = { NULL };
static void *cbparam[8] = { NULL };
const gpio_cfg_t pe_int_pin = { PORT_1, PIN_7, GPIO_FUNC_IN, GPIO_PAD_PULL_UP };
static const portexpander_cfg_t pe_pin_config[] = {
{ PE_INPUT_MASK, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },
{ ~PE_INPUT_MASK, GPIO_FUNC_OUT, GPIO_PAD_PULL_UP },
};
/* **** Functions **** */
static int portexpander_write(uint8_t command, uint8_t data)
{
uint8_t i2c_data[2] = { command, data };
return I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, i2c_data, 2, 0);
}
/* ************************************************************************** */
static int portexpander_read(uint8_t command, uint8_t *data)
{
I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, &command, 1, 1);
return I2C_MasterRead(MXC_I2C1_BUS0, PE_ADDR, data, 1, 0);
}
void portexpander_init(void)
/* ************************************************************************** */
int portexpander_init(void)
{
int ret;
// Enable pull-ups for buttons (type defaults to pull-up)
ret = portexpander_write(PE_C_PULL_ENABLE, PE_INPUT_MASK);
// Set _all_ outputs to open-drain to support the high side p-channel transistors.
ret = portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN);
if (ret != 2) {
printf("portexpander NOT detected\n");
detected = false;
return;
return E_NO_DEVICE;
}
detected = true;
// Set _all_ outputs to open-drain to support the high side p-channel transistors.
portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN);
// Set outputs to high
portexpander_out_set(~PE_INPUT_MASK);
// Enable pull-ups for buttons
// Enable outputs for the transistors, the LED and the LCD reset
portexpander_write(PE_C_CONFIG, PE_INPUT_MASK);
for (int i = 0; i < sizeof(pe_pin_config) / sizeof(pe_pin_config[0]);
i++) {
MXC_ASSERT(
portexpander_config(&pe_pin_config[i]) == E_NO_ERROR
);
}
// Set outputs to high (i.e. open-drain)
output_state = ~PE_INPUT_MASK;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
// Latch inputs so we can figure out whether an interrupt was caused by a rising or falling edge
portexpander_write(PE_C_INPUT_LATCH, PE_INPUT_MASK);
// Configure interrupt GPIO
MXC_ASSERT(GPIO_Config(&pe_int_pin) == E_NO_ERROR);
// Configure and enable portexpander interrupt
GPIO_RegisterCallback(
&pe_int_pin, &portexpander_interrupt_callback, NULL
);
MXC_ASSERT(
GPIO_IntConfig(&pe_int_pin, GPIO_INT_EDGE, GPIO_INT_FALLING) ==
E_NO_ERROR);
GPIO_IntEnable(&pe_int_pin);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(pe_int_pin.port));
return E_SUCCESS;
}
/* ************************************************************************** */
int portexpander_config(const portexpander_cfg_t *cfg)
{
// Set the GPIO type
switch (cfg->func) {
case GPIO_FUNC_IN:
type_state |= cfg->mask;
break;
case GPIO_FUNC_OUT:
type_state &= ~cfg->mask;
break;
default:
return E_BAD_PARAM;
}
if (portexpander_write(PE_C_CONFIG, type_state) != 2) {
return E_NO_DEVICE;
}
switch (cfg->pad) {
case GPIO_PAD_NONE:
pull_enable_state &= ~cfg->mask;
break;
case GPIO_PAD_PULL_UP:
pull_selection_state |= cfg->mask;
pull_enable_state |= cfg->mask;
break;
case GPIO_PAD_PULL_DOWN:
pull_selection_state &= ~cfg->mask;
pull_enable_state |= cfg->mask;
break;
default:
return E_BAD_PARAM;
}
portexpander_write(PE_C_PULL_ENABLE, pull_selection_state);
portexpander_write(PE_C_PULL_ENABLE, pull_enable_state);
return E_NO_ERROR;
}
uint8_t portexpander_get(void)
/* ************************************************************************** */
uint8_t portexpander_in_get(uint8_t mask)
{
// Reading the input port clears interrupts, so we need to check them here to avoid losing information
portexpander_poll();
uint8_t buf = 0xFF;
if (detected) {
portexpander_read(PE_C_INPUT_PORT, &buf);
}
return buf;
return buf & mask;
}
/* ************************************************************************** */
bool portexpander_detected(void)
{
return detected;
......@@ -106,6 +201,24 @@ void portexpander_set(uint8_t pin, uint8_t value)
}
}
/* ************************************************************************** */
void portexpander_out_set(uint8_t mask)
{
if (detected) {
output_state |= mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
/* ************************************************************************** */
void portexpander_out_clr(uint8_t mask)
{
if (detected) {
output_state &= ~mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
void portexpander_prep(uint8_t pin, uint8_t value)
{
if (pin < 8) {
......@@ -117,6 +230,12 @@ void portexpander_prep(uint8_t pin, uint8_t value)
}
}
/* ************************************************************************** */
uint8_t portexpander_out_get(uint8_t mask)
{
return output_state & mask;
}
void portexpander_update(void)
{
if (detected) {
......@@ -124,11 +243,126 @@ void portexpander_update(void)
}
}
void portexpander_set_mask(uint8_t mask, uint8_t values)
/* ************************************************************************** */
void portexpander_out_put(uint8_t mask, uint8_t val)
{
if (detected) {
output_state = (output_state & ~mask) | (val & mask);
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
/* ************************************************************************** */
void portexpander_out_toggle(uint8_t mask)
{
if (detected) {
output_state &= ~(mask & ~values);
output_state |= mask & values;
output_state ^= mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
/* ************************************************************************** */
void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge)
{
if (detected) {
for (uint8_t pin = 0; pin < 8; ++pin) {
if (mask & (1 << pin)) {
int_edge_config[pin] = edge;
}
}
}
}
/* ************************************************************************** */
void portexpander_int_enable(uint8_t mask)
{
if (detected) {
int_mask_state &= ~mask;
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
void portexpander_int_disable(uint8_t mask)
{
if (detected) {
int_mask_state |= mask;
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
uint8_t portexpander_int_status()
{
uint8_t buf = 0;
if (detected) {
portexpander_read(PE_C_INT_STATUS, &buf);
}
return buf;
}
/* ************************************************************************** */
void portexpander_int_clr(uint8_t mask)
{
if (detected) {
uint8_t tmp_mask = int_mask_state | mask;
// Setting an interrupt mask clears the corresponding interrupt
portexpander_write(PE_C_INT_MASK, tmp_mask);
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
int portexpander_register_callback(
uint8_t mask, pe_callback callback, void *cbdata
) {
if (!detected) {
return E_NO_DEVICE;
}
for (uint8_t pin = 0; pin < 8; ++pin) {
if (mask & (1 << pin)) {
callbacks[pin] = callback;
cbparam[pin] = cbdata;
}
}
return E_NO_ERROR;
}
/* ************************************************************************** */
__attribute__((weak)) void portexpander_interrupt_callback(void *_)
{
GPIO_IntDisable(&pe_int_pin);
GPIO_IntClr(&pe_int_pin);
interrupt_pending = true;
}
/* ************************************************************************** */
void portexpander_poll()
{
if (detected && interrupt_pending) {
interrupt_pending = false;
uint8_t caused_by = portexpander_int_status();
// Port read resets interrupts
uint8_t port_levels = portexpander_in_get(0xFF);
GPIO_IntEnable(&pe_int_pin);
for (uint8_t pin = 0; pin < 8; ++pin) {
if ((caused_by & (1 << pin)) && callbacks[pin]) {
gpio_int_pol_t edge_type =
(port_levels & (1 << pin) ?
GPIO_INT_RISING :
GPIO_INT_FALLING);
if ((int_edge_config[pin] == GPIO_INT_BOTH) ||
(edge_type == int_edge_config[pin])) {
callbacks[pin](cbparam[pin]);
}
}
}
}
}
#ifndef PORTEXPANDER_H
#define PORTEXPANDER_H
#include "mxc_config.h"
#include <stdint.h>
#include <stdbool.h>
void portexpander_init(void);
uint8_t portexpander_get(void);
void portexpander_set(uint8_t pin, uint8_t value);
void portexpander_set_mask(uint8_t mask, uint8_t values);
void portexpander_prep(uint8_t pin, uint8_t value);
void portexpander_update(void);
/**
* Structure type for configuring the portexpander.
*/
typedef struct {
uint8_t mask; /**< Pin mask (multiple pins may be set) */
gpio_func_t func; /**< Function type */
gpio_pad_t pad; /**< Pad type */
} portexpander_cfg_t;
typedef void (*pe_callback)(void *cbdata);
int portexpander_init(void);
bool portexpander_detected(void);
int portexpander_config(const portexpander_cfg_t *cfg);
uint8_t portexpander_in_get(uint8_t mask);
void portexpander_out_set(uint8_t mask);
void portexpander_out_clr(uint8_t mask);
void portexpander_out_put(uint8_t mask, uint8_t val);
void portexpander_out_toggle(uint8_t mask);
uint8_t portexpander_out_get(uint8_t mask);
void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge);
void portexpander_int_enable(uint8_t mask);
void portexpander_int_disable(uint8_t mask);
uint8_t portexpander_int_status();
void portexpander_int_clr(uint8_t mask);
int portexpander_register_callback(uint8_t mask, pe_callback callback, void *cbdata);
void portexpander_poll();
void portexpander_interrupt_callback(void *_);
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment