drivers: gpio: Add LiteX GPIO driver

This commits adds GPIO driver for LiteX SoC builder.

Due to the fact that GPIO in LiteX is unidirectional and can be
configured with different pins amount per port, additional entries
were added to the dts file.

Signed-off-by: Robert Winkler <rwinkler@internships.antmicro.com>
Signed-off-by: Mateusz Holenko <mholenko@antmicro.com>
Signed-off-by: Piotr Zierhoffer <pzierhoffer@antmicro.com>
This commit is contained in:
Robert Winkler 2019-08-02 10:38:56 +02:00 committed by Carles Cufí
parent c0cf714087
commit e8d0eb1db1
7 changed files with 397 additions and 0 deletions

View file

@ -149,6 +149,7 @@
/drivers/gpio/*lmp90xxx* @henrikbrixandersen
/drivers/gpio/*stm32* @erwango
/drivers/gpio/*sx1509b* @pabigot
/drivers/gpio/*litex* @mateusz-holenko @kgugala @pgielda
/drivers/hwinfo/ @alexanderwachter
/drivers/i2c/*litex* @mateusz-holenko @kgugala @pgielda
/drivers/i2s/i2s_ll_stm32* @avisconti

View file

@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_STELLARIS gpio_stellaris.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_RV32M1 gpio_rv32m1.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_HT16K33 gpio_ht16k33.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LMP90XXX gpio_lmp90xxx.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LITEX gpio_litex.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)

View file

@ -67,4 +67,6 @@ source "drivers/gpio/Kconfig.ht16k33"
source "drivers/gpio/Kconfig.lmp90xxx"
source "drivers/gpio/Kconfig.litex"
endif # GPIO

View file

@ -0,0 +1,11 @@
# Litex VexRiscV GPIO configuration options
# Copyright (c) 2019 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
config GPIO_LITEX
bool "Litex GPIO driver"
depends on SOC_RISCV32_LITEX_VEXRISCV
select HAS_DTS_GPIO
help
Enable Litex GPIO driver.

315
drivers/gpio/gpio_litex.c Normal file
View file

@ -0,0 +1,315 @@
/*
* Copyright (c) 2019 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <device.h>
#include <drivers/gpio.h>
#include <zephyr/types.h>
#include <sys/util.h>
#include <string.h>
#include <logging/log.h>
#define SUPPORTED_FLAGS (GPIO_INPUT | GPIO_OUTPUT | \
GPIO_OUTPUT_INIT_LOW | GPIO_OUTPUT_INIT_HIGH | \
GPIO_ACTIVE_LOW | GPIO_ACTIVE_HIGH)
#define GPIO_LOW 0
#define GPIO_HIGH 1
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
LOG_MODULE_REGISTER(gpio_litex);
static const char *LITEX_LOG_REG_SIZE_NGPIOS_MISMATCH =
"Cannot handle all of the gpios with the register of given size\n";
static const char *LITEX_LOG_WRONG_DIR =
"Direction chosen in device tree do not match with the operation\n";
static const char *LITEX_LOG_CANNOT_CHANGE_DIR =
"Cannot change port direction selected in device tree\n";
struct gpio_litex_cfg {
volatile u32_t *reg_addr;
int reg_size;
int nr_gpios;
bool port_is_output;
};
struct gpio_litex_data {
struct gpio_driver_data common;
};
/* Helper macros for GPIO */
#define DEV_GPIO_CFG(dev) \
((const struct gpio_litex_cfg *)(dev)->config->config_info)
/* Helper functions for bit / port access */
static inline void set_bit(const struct gpio_litex_cfg *config,
u32_t bit, bool val)
{
int regv, new_regv;
regv = litex_read(config->reg_addr, config->reg_size);
new_regv = (regv & ~BIT(bit)) | (val << bit);
litex_write(config->reg_addr, config->reg_size, new_regv);
}
static inline u32_t get_bit(const struct gpio_litex_cfg *config, u32_t bit)
{
int regv = litex_read(config->reg_addr, config->reg_size);
return !!(regv & BIT(bit));
}
static inline void set_port(const struct gpio_litex_cfg *config, u32_t value)
{
litex_write(config->reg_addr, config->reg_size, value);
}
static inline u32_t get_port(const struct gpio_litex_cfg *config)
{
int regv = litex_read(config->reg_addr, config->reg_size);
return (regv & BIT_MASK(config->nr_gpios));
}
/* Driver functions */
static int gpio_litex_init(struct device *dev)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
/* each 4-byte register is able to handle 8 GPIO pins */
if (gpio_config->nr_gpios > (gpio_config->reg_size / 4) * 8) {
LOG_ERR("%s", LITEX_LOG_REG_SIZE_NGPIOS_MISMATCH);
return -EINVAL;
}
return 0;
}
static int gpio_litex_configure(struct device *dev, int access_op,
u32_t pin, int flags)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
if (access_op != GPIO_ACCESS_BY_PIN) {
return -ENOTSUP;
}
if (flags & ~SUPPORTED_FLAGS) {
return -ENOTSUP;
}
if ((flags & GPIO_OUTPUT) && (flags & GPIO_INPUT)) {
/* Pin cannot be configured as input and output */
return -ENOTSUP;
} else if (!(flags & (GPIO_INPUT | GPIO_OUTPUT))) {
/* Pin has to be configuread as input or output */
return -ENOTSUP;
}
if (flags & GPIO_OUTPUT) {
if (!gpio_config->port_is_output) {
LOG_ERR("%s", LITEX_LOG_CANNOT_CHANGE_DIR);
return -EINVAL;
}
if (flags & GPIO_OUTPUT_INIT_HIGH) {
set_bit(gpio_config, pin, GPIO_HIGH);
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
set_bit(gpio_config, pin, GPIO_LOW);
}
} else {
if (gpio_config->port_is_output) {
LOG_ERR("%s", LITEX_LOG_CANNOT_CHANGE_DIR);
return -EINVAL;
}
}
return 0;
}
static int gpio_litex_write(struct device *dev, int access_op,
u32_t pin, u32_t value)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
if (!gpio_config->port_is_output) {
LOG_ERR("%s", LITEX_LOG_WRONG_DIR);
return -EINVAL;
}
if (access_op != GPIO_ACCESS_BY_PIN) {
return -ENOTSUP;
}
set_bit(gpio_config, pin, value);
return 0;
}
static int gpio_litex_read(struct device *dev, int access_op,
u32_t pin, u32_t *value)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
if (access_op != GPIO_ACCESS_BY_PIN) {
return -ENOTSUP;
}
*value = get_bit(gpio_config, pin);
return 0;
}
static int gpio_litex_port_get_raw(struct device *dev, gpio_port_value_t *value)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
*value = get_port(gpio_config);
return 0;
}
static int gpio_litex_port_set_masked_raw(struct device *dev,
gpio_port_pins_t mask,
gpio_port_value_t value)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
u32_t port_val;
port_val = get_port(gpio_config);
port_val = (port_val & ~mask) | (value & mask);
set_port(gpio_config, port_val);
return 0;
}
static int gpio_litex_port_set_bits_raw(struct device *dev,
gpio_port_pins_t pins)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
u32_t port_val;
port_val = get_port(gpio_config);
port_val |= pins;
set_port(gpio_config, port_val);
return 0;
}
static int gpio_litex_port_clear_bits_raw(struct device *dev,
gpio_port_pins_t pins)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
u32_t port_val;
port_val = get_port(gpio_config);
port_val &= ~pins;
set_port(gpio_config, port_val);
return 0;
}
static int gpio_litex_port_toggle_bits(struct device *dev,
gpio_port_pins_t pins)
{
const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev);
u32_t port_val;
port_val = get_port(gpio_config);
port_val ^= pins;
set_port(gpio_config, port_val);
return 0;
}
static int gpio_litex_pin_interrupt_configure(struct device *dev,
unsigned int pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
int ret = 0;
if (mode != GPIO_INT_MODE_DISABLED) {
ret = -ENOTSUP;
}
return ret;
}
static const struct gpio_driver_api gpio_litex_driver_api = {
.config = gpio_litex_configure,
.write = gpio_litex_write,
.read = gpio_litex_read,
.port_get_raw = gpio_litex_port_get_raw,
.port_set_masked_raw = gpio_litex_port_set_masked_raw,
.port_set_bits_raw = gpio_litex_port_set_bits_raw,
.port_clear_bits_raw = gpio_litex_port_clear_bits_raw,
.port_toggle_bits = gpio_litex_port_toggle_bits,
.pin_interrupt_configure = gpio_litex_pin_interrupt_configure,
};
/* Device Instantiation */
#define GPIO_LITEX_INIT(n) \
BUILD_ASSERT_MSG(DT_INST_##n##_LITEX_GPIO_SIZE != 0 \
&& DT_INST_##n##_LITEX_GPIO_SIZE % 4 == 0, \
"Register size must be a multiple of 4"); \
\
static const struct gpio_litex_cfg gpio_litex_cfg_##n = { \
.reg_addr = \
(volatile u32_t *) DT_INST_##n##_LITEX_GPIO_BASE_ADDRESS, \
.reg_size = DT_INST_##n##_LITEX_GPIO_SIZE, \
.nr_gpios = DT_INST_##n##_LITEX_GPIO_NGPIOS, \
.port_is_output = DT_INST_##n##_LITEX_GPIO_PORT_IS_OUTPUT, \
}; \
static struct gpio_litex_data gpio_litex_data_##n; \
\
DEVICE_AND_API_INIT(litex_gpio_##n, \
DT_INST_##n##_LITEX_GPIO_LABEL, \
gpio_litex_init, \
&gpio_litex_data_##n, \
&gpio_litex_cfg_##n, \
POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&gpio_litex_driver_api \
)
#ifdef DT_INST_0_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(0);
#endif
#ifdef DT_INST_1_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(1);
#endif
#ifdef DT_INST_2_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(2);
#endif
#ifdef DT_INST_3_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(3);
#endif
#ifdef DT_INST_4_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(4);
#endif
#ifdef DT_INST_5_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(5);
#endif
#ifdef DT_INST_6_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(6);
#endif
#ifdef DT_INST_7_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(7);
#endif
#ifdef DT_INST_8_LITEX_GPIO_LABEL
GPIO_LITEX_INIT(8);
#endif

View file

@ -0,0 +1,35 @@
#
# Copyright (c) 2019 Antmicro <www.antmicro.com>
#
# SPDX-License-Identifier: Apache-2.0
#
title: Litex GPIO
description: >
This is a representation of the Litex GPIO nodes
compatible: "litex,gpio"
include: [gpio-controller.yaml, base.yaml]
properties:
port-is-output:
type: boolean
description: Indicates if the port is an output port
reg:
required: true
label:
required: true
ngpios:
required: true
"#gpio-cells":
const: 2
gpio-cells:
- pin
- flags

View file

@ -54,6 +54,38 @@ static inline void litex_write32(unsigned int value, unsigned long addr)
sys_write8(value >> 8, addr + 0x8);
sys_write8(value, addr + 0xC);
}
/* `reg_size` is assumed to be a multiple of 4 */
static inline void litex_write(volatile u32_t *reg, u32_t reg_size, u32_t val)
{
u32_t shifted_data;
volatile u32_t *reg_addr;
u32_t subregs = reg_size / 4;
for (int i = 0; i < subregs; ++i) {
shifted_data = val >> ((subregs - i - 1) * 8);
reg_addr = ((volatile u32_t *) reg) + i;
*(reg_addr) = shifted_data;
}
}
/* `reg_size` is assumed to be a multiple of 4 */
static inline u32_t litex_read(volatile u32_t *reg, u32_t reg_size)
{
u32_t shifted_data;
volatile u32_t *reg_addr;
u32_t result = 0;
u32_t subregs = reg_size / 4;
for (int i = 0; i < subregs; ++i) {
reg_addr = ((volatile u32_t *) reg) + i;
shifted_data = *(reg_addr);
result |= (shifted_data << ((subregs - i - 1) * 8));
}
return result;
}
#endif /* _ASMLANGUAGE */
#endif /* __RISCV32_LITEX_VEXRISCV_SOC_H_ */