281 lines
6.1 KiB
C
281 lines
6.1 KiB
C
/*
|
|
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file Driver for the pulpino GPIO controller.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <soc.h>
|
|
#include <gpio.h>
|
|
#include <misc/util.h>
|
|
|
|
#include "gpio_utils.h"
|
|
|
|
typedef void (*pulpino_cfg_func_t)(void);
|
|
|
|
/* pulpino GPIO register-set structure */
|
|
struct gpio_pulpino_t {
|
|
uint32_t paddir;
|
|
uint32_t padin;
|
|
uint32_t padout;
|
|
uint32_t inten;
|
|
uint32_t inttype0;
|
|
uint32_t inttype1;
|
|
uint32_t intstatus;
|
|
};
|
|
|
|
struct gpio_pulpino_config {
|
|
uint32_t gpio_base_addr;
|
|
pulpino_cfg_func_t gpio_cfg_func;
|
|
};
|
|
|
|
struct gpio_pulpino_data {
|
|
/* list of callbacks */
|
|
sys_slist_t cb;
|
|
};
|
|
|
|
/* Helper Macros for GPIO */
|
|
#define DEV_GPIO_CFG(dev) \
|
|
((const struct gpio_pulpino_config * const)(dev)->config->config_info)
|
|
#define DEV_GPIO(dev) \
|
|
((volatile struct gpio_pulpino_t *)(DEV_GPIO_CFG(dev))->gpio_base_addr)
|
|
#define DEV_GPIO_DATA(dev) \
|
|
((struct gpio_pulpino_data *)(dev)->driver_data)
|
|
|
|
static void gpio_pulpino_irq_handler(void *arg)
|
|
{
|
|
struct device *dev = (struct device *)arg;
|
|
struct gpio_pulpino_data *data = DEV_GPIO_DATA(dev);
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
_gpio_fire_callbacks(&data->cb, dev, gpio->intstatus);
|
|
}
|
|
|
|
/**
|
|
* @brief Configure pin
|
|
*
|
|
* @param dev Device structure
|
|
* @param access_op Access operation
|
|
* @param pin The pin number
|
|
* @param flags Flags of pin or port
|
|
*
|
|
* @return 0 if successful, failed otherwise
|
|
*/
|
|
static int gpio_pulpino_config(struct device *dev,
|
|
int access_op,
|
|
uint32_t pin,
|
|
int flags)
|
|
{
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN)
|
|
return -ENOTSUP;
|
|
|
|
if (pin > 31)
|
|
return -ENODEV;
|
|
|
|
/* Configure pin as gpio */
|
|
PULP_PADMUX |= (PULP_PAD_GPIO << pin);
|
|
|
|
/* Configure gpio direction */
|
|
if (flags & GPIO_DIR_OUT)
|
|
gpio->paddir |= BIT(pin);
|
|
else
|
|
gpio->paddir &= ~BIT(pin);
|
|
|
|
/*
|
|
* Configure interrupt if GPIO_INT is set.
|
|
* Here, we just configure the gpio interrupt behavior,
|
|
* we do not enable/disable interrupt for a particular
|
|
* gpio.
|
|
* Interrupt for a gpio is:
|
|
* 1) enabled only via a call to gpio_pulpino_enable_callback.
|
|
* 2) disabled only via a call to gpio_pulpino_disabled_callback.
|
|
*/
|
|
if (!(flags & GPIO_INT))
|
|
return 0;
|
|
|
|
/*
|
|
* Interrupt cannot be set for GPIO_DIR_OUT
|
|
*/
|
|
if (flags & GPIO_DIR_OUT)
|
|
return -EINVAL;
|
|
|
|
/* Double edge trigger not supported */
|
|
if (flags & GPIO_INT_DOUBLE_EDGE)
|
|
return -ENOTSUP;
|
|
|
|
/* Edge or Level triggered ? */
|
|
if (flags & GPIO_INT_EDGE)
|
|
gpio->inttype1 |= BIT(pin);
|
|
else
|
|
gpio->inttype1 &= ~BIT(pin);
|
|
|
|
/* Level High/Rising Edge ? */
|
|
if (flags & GPIO_INT_ACTIVE_HIGH)
|
|
gpio->inttype0 &= ~BIT(pin);
|
|
else
|
|
gpio->inttype0 |= BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the pin
|
|
*
|
|
* @param dev Device struct
|
|
* @param access_op Access operation
|
|
* @param pin The pin number
|
|
* @param value Value to set (0 or 1)
|
|
*
|
|
* @return 0 if successful, failed otherwise
|
|
*/
|
|
static int gpio_pulpino_write(struct device *dev,
|
|
int access_op,
|
|
uint32_t pin,
|
|
uint32_t value)
|
|
{
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN)
|
|
return -ENOTSUP;
|
|
|
|
if (value)
|
|
gpio->padout |= BIT(pin);
|
|
else
|
|
gpio->padout &= ~BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the pin
|
|
*
|
|
* @param dev Device struct
|
|
* @param access_op Access operation
|
|
* @param pin The pin number
|
|
* @param value Value of input pin(s)
|
|
*
|
|
* @return 0 if successful, failed otherwise
|
|
*/
|
|
static int gpio_pulpino_read(struct device *dev,
|
|
int access_op,
|
|
uint32_t pin,
|
|
uint32_t *value)
|
|
{
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN)
|
|
return -ENOTSUP;
|
|
|
|
/*
|
|
* If gpio is configured as output,
|
|
* read gpio value from padout register,
|
|
* otherwise read gpio value from padin register
|
|
*/
|
|
if (gpio->paddir & BIT(pin))
|
|
*value = !!(gpio->padout & BIT(pin));
|
|
else
|
|
*value = !!(gpio->padin & BIT(pin));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_pulpino_manage_callback(struct device *dev,
|
|
struct gpio_callback *callback,
|
|
bool set)
|
|
{
|
|
struct gpio_pulpino_data *data = DEV_GPIO_DATA(dev);
|
|
|
|
_gpio_manage_callback(&data->cb, callback, set);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_pulpino_enable_callback(struct device *dev,
|
|
int access_op,
|
|
uint32_t pin)
|
|
{
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN)
|
|
return -ENOTSUP;
|
|
|
|
/* Enable interrupt for pin */
|
|
gpio->inten |= BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_pulpino_disable_callback(struct device *dev,
|
|
int access_op,
|
|
uint32_t pin)
|
|
{
|
|
volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev);
|
|
|
|
if (access_op != GPIO_ACCESS_BY_PIN)
|
|
return -ENOTSUP;
|
|
|
|
/* Disable interrupt for pin */
|
|
gpio->inten &= ~BIT(pin);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct gpio_driver_api gpio_pulpino_driver = {
|
|
.config = gpio_pulpino_config,
|
|
.write = gpio_pulpino_write,
|
|
.read = gpio_pulpino_read,
|
|
.manage_callback = gpio_pulpino_manage_callback,
|
|
.enable_callback = gpio_pulpino_enable_callback,
|
|
.disable_callback = gpio_pulpino_disable_callback,
|
|
};
|
|
|
|
/**
|
|
* @brief Initialize a GPIO controller
|
|
*
|
|
* Perform basic initialization of a GPIO controller
|
|
*
|
|
* @param dev GPIO device struct
|
|
*
|
|
* @return 0
|
|
*/
|
|
static int gpio_pulpino_init(struct device *dev)
|
|
{
|
|
const struct gpio_pulpino_config *cfg = DEV_GPIO_CFG(dev);
|
|
|
|
cfg->gpio_cfg_func();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_pulpino_cfg_0(void);
|
|
|
|
static const struct gpio_pulpino_config gpio_pulpino_config0 = {
|
|
.gpio_base_addr = PULP_GPIO_0_BASE,
|
|
.gpio_cfg_func = gpio_pulpino_cfg_0,
|
|
};
|
|
|
|
static struct gpio_pulpino_data gpio_pulpino_data0;
|
|
|
|
DEVICE_AND_API_INIT(gpio_pulpino_0, "gpio0", gpio_pulpino_init,
|
|
&gpio_pulpino_data0, &gpio_pulpino_config0,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
&gpio_pulpino_driver);
|
|
|
|
static void gpio_pulpino_cfg_0(void)
|
|
{
|
|
IRQ_CONNECT(PULP_GPIO_0_IRQ,
|
|
0,
|
|
gpio_pulpino_irq_handler,
|
|
DEVICE_GET(gpio_pulpino_0),
|
|
0);
|
|
irq_enable(PULP_GPIO_0_IRQ);
|
|
}
|