drivers: gpio: Xilinx PS MIO / EMIO GPIO driver
Driver implementation for the Xilinx Processor System MIO / EMIO GPIO controller as contained in the Zynq-7000 and ZynqMP (UltraScale) SoCs. The driver is split up into source and header for a parent controller device and source and header for 1..n child GPIO pin bank devices. The parent device driver takes care of IRQ handling, the GPIO pin bank driver provides pin / bank access according to the API defined by the GPIO subsystem. More than one device for this type of GPIO controller is required as it provides access to a number of GPIO pins well in excess of the 32 pins addressable by the current GPIO API (whereever parameters or return values come in the form of a bit mask): - Zynq-7000: 54 MIO GPIO pins, 64 EMIO GPIO pins in 4 banks. - ZynqMP: 78 MIO GPIO pins, 96 EMIO GPIO pins in 6 banks. Signed-off-by: Immo Birnbaum <Immo.Birnbaum@weidmueller.com>
This commit is contained in:
parent
cc95910957
commit
ca33905248
|
@ -55,6 +55,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX gpio_nct38xx_port.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX_INTERRUPT gpio_nct38xx_alert.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_TEST gpio_test.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_GD32 gpio_gd32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_PS gpio_xlnx_ps.c gpio_xlnx_ps_bank.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
||||
|
||||
|
|
|
@ -121,4 +121,6 @@ source "drivers/gpio/Kconfig.test"
|
|||
|
||||
source "drivers/gpio/Kconfig.gd32"
|
||||
|
||||
source "drivers/gpio/Kconfig.xlnx_ps"
|
||||
|
||||
endif # GPIO
|
||||
|
|
17
drivers/gpio/Kconfig.xlnx_ps
Normal file
17
drivers/gpio/Kconfig.xlnx_ps
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Xilinx Processor System MIO / EMIO GPIO controller driver
|
||||
# configuration options
|
||||
#
|
||||
# Copyright (c) 2022, Weidmueller Interface GmbH & Co. KG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
DT_COMPAT_XLNX_PS_GPIO := xlnx,ps-gpio
|
||||
|
||||
config GPIO_XLNX_PS
|
||||
bool "Xilinx Processor System MIO / EMIO GPIO controller driver"
|
||||
default $(dt_compat_enabled,$(DT_COMPAT_XLNX_PS_GPIO))
|
||||
depends on SOC_XILINX_ZYNQMP_RPU || SOC_FAMILY_XILINX_ZYNQ7000
|
||||
depends on !QEMU_TARGET
|
||||
help
|
||||
Enable the Xilinx Processor System MIO / EMIO GPIO controller driver.
|
151
drivers/gpio/gpio_xlnx_ps.c
Normal file
151
drivers/gpio/gpio_xlnx_ps.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Xilinx Processor System MIO / EMIO GPIO controller driver
|
||||
* Parent (IRQ handler) module
|
||||
*
|
||||
* Copyright (c) 2022, Weidmueller Interface GmbH & Co. KG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <devicetree.h>
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include "gpio_utils.h"
|
||||
#include "gpio_xlnx_ps.h"
|
||||
#include "gpio_xlnx_ps_bank.h"
|
||||
|
||||
#define LOG_MODULE_NAME gpio_xlnx_ps
|
||||
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#define DT_DRV_COMPAT xlnx_ps_gpio
|
||||
|
||||
/*
|
||||
* An API is required for this driver, but as no pin access is provided at
|
||||
* this level, use the default API contents provided by the driver subsystem.
|
||||
*/
|
||||
static const struct gpio_driver_api gpio_xlnx_ps_default_apis;
|
||||
|
||||
/**
|
||||
* @brief Initialize a Xilinx PS GPIO controller parent device
|
||||
*
|
||||
* Initialize a Xilinx PS GPIO controller parent device, whose task it is
|
||||
* to handle the IRQ line of each controller instance, while the configuration,
|
||||
* status and data acquisition of each MIO / EMIO GPIO pin associated with
|
||||
* the parent controller instance is handled via the respective GPIO pin
|
||||
* bank's child device.
|
||||
*
|
||||
* @param dev Pointer to the PS GPIO controller's device.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_init(const struct device *dev)
|
||||
{
|
||||
const struct gpio_xlnx_ps_dev_cfg *dev_conf = dev->config;
|
||||
|
||||
/* Initialize the device's interrupt */
|
||||
dev_conf->config_func(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Xilinx PS GPIO controller parent device ISR
|
||||
*
|
||||
* Interrupt service routine for the Xilinx PS GPIO controller's
|
||||
* IRQ. The ISR iterates all associated MIO / EMIO GPIO pink bank
|
||||
* child device instances and checks each bank's interrupt status.
|
||||
* If any pending interrupt is detected within a GPIO pin bank,
|
||||
* the callbacks registered for the respective bank are triggered
|
||||
* using the functionality provided by the GPIO sub-system.
|
||||
*
|
||||
* @param dev Pointer to the PS GPIO controller's device.
|
||||
*/
|
||||
static void gpio_xlnx_ps_isr(const struct device *dev)
|
||||
{
|
||||
const struct gpio_xlnx_ps_dev_cfg *dev_conf = dev->config;
|
||||
|
||||
const struct gpio_driver_api *api;
|
||||
struct gpio_xlnx_ps_bank_dev_data *bank_data;
|
||||
|
||||
uint32_t bank;
|
||||
uint32_t int_mask;
|
||||
|
||||
for (bank = 0; bank < dev_conf->num_banks; bank++) {
|
||||
api = dev_conf->bank_devices[bank]->api;
|
||||
int_mask = 0;
|
||||
|
||||
if (api != NULL) {
|
||||
int_mask = api->get_pending_int(dev_conf->bank_devices[bank]);
|
||||
}
|
||||
if (int_mask) {
|
||||
bank_data = (struct gpio_xlnx_ps_bank_dev_data *)
|
||||
dev_conf->bank_devices[bank]->data;
|
||||
gpio_fire_callbacks(&bank_data->callbacks,
|
||||
dev_conf->bank_devices[bank], int_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Device definition macros */
|
||||
|
||||
/*
|
||||
* Macros generating a list of all associated GPIO pin bank child
|
||||
* devices for the parent controller device's config data struct
|
||||
* specified in the device tree.
|
||||
*/
|
||||
#define GPIO_XLNX_PS_CHILD_CONCAT(idx) DEVICE_DT_GET(idx),
|
||||
|
||||
#define GPIO_XLNX_PS_GEN_BANK_ARRAY(idx)\
|
||||
static const struct device *gpio_xlnx_ps##idx##_banks[] = {\
|
||||
DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(idx), GPIO_XLNX_PS_CHILD_CONCAT)\
|
||||
};
|
||||
|
||||
/* Device config & run-time data struct creation macros */
|
||||
#define GPIO_XLNX_PS_DEV_DATA(idx)\
|
||||
static struct gpio_xlnx_ps_dev_data gpio_xlnx_ps##idx##_data;
|
||||
|
||||
#define GPIO_XLNX_PS_DEV_CONFIG(idx)\
|
||||
static const struct gpio_xlnx_ps_dev_cfg gpio_xlnx_ps##idx##_cfg = {\
|
||||
.base_addr = DT_INST_REG_ADDR(idx),\
|
||||
.bank_devices = gpio_xlnx_ps##idx##_banks,\
|
||||
.num_banks = ARRAY_SIZE(gpio_xlnx_ps##idx##_banks),\
|
||||
.config_func = gpio_xlnx_ps##idx##_irq_config\
|
||||
};
|
||||
|
||||
/*
|
||||
* Macro used to generate each parent controller device's IRQ attach
|
||||
* function.
|
||||
*/
|
||||
#define GPIO_XLNX_PS_DEV_CONFIG_IRQ_FUNC(idx)\
|
||||
static void gpio_xlnx_ps##idx##_irq_config(const struct device *dev)\
|
||||
{\
|
||||
ARG_UNUSED(dev);\
|
||||
IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority),\
|
||||
gpio_xlnx_ps_isr, DEVICE_DT_INST_GET(idx), 0);\
|
||||
irq_enable(DT_INST_IRQN(idx));\
|
||||
}
|
||||
|
||||
/* Device definition macro */
|
||||
#define GPIO_XLNX_PS_DEV_DEFINE(idx)\
|
||||
DEVICE_DT_INST_DEFINE(idx, gpio_xlnx_ps_init, NULL,\
|
||||
&gpio_xlnx_ps##idx##_data, &gpio_xlnx_ps##idx##_cfg,\
|
||||
PRE_KERNEL_2, CONFIG_GPIO_INIT_PRIORITY, &gpio_xlnx_ps_default_apis);
|
||||
|
||||
/*
|
||||
* Top-level device initialization macro, executed for each PS GPIO
|
||||
* parent device entry contained in the device tree which has status
|
||||
* "okay".
|
||||
*/
|
||||
#define GPIO_XLNX_PS_DEV_INITITALIZE(idx)\
|
||||
GPIO_XLNX_PS_GEN_BANK_ARRAY(idx)\
|
||||
GPIO_XLNX_PS_DEV_CONFIG_IRQ_FUNC(idx)\
|
||||
GPIO_XLNX_PS_DEV_DATA(idx)\
|
||||
GPIO_XLNX_PS_DEV_CONFIG(idx)\
|
||||
GPIO_XLNX_PS_DEV_DEFINE(idx)
|
||||
|
||||
/*
|
||||
* Register & initialize all instances of the Processor System's MIO / EMIO GPIO
|
||||
* controller specified in the device tree.
|
||||
*/
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_XLNX_PS_DEV_INITITALIZE);
|
45
drivers/gpio/gpio_xlnx_ps.h
Normal file
45
drivers/gpio/gpio_xlnx_ps.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Xilinx Processor System MIO / EMIO GPIO controller driver
|
||||
*
|
||||
* Driver private data declarations, parent (IRQ handler) module
|
||||
*
|
||||
* Copyright (c) 2022, Weidmueller Interface GmbH & Co. KG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_H_
|
||||
#define _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_H_
|
||||
|
||||
/* Type definitions */
|
||||
|
||||
/* IRQ handler function type */
|
||||
typedef void (*gpio_xlnx_ps_config_irq_t)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Run-time modifiable device data structure.
|
||||
*
|
||||
* This struct contains all data of the PS GPIO controller parent
|
||||
* (IRQ handler) which is modifiable at run-time.
|
||||
*/
|
||||
struct gpio_xlnx_ps_dev_data {
|
||||
struct gpio_driver_data common;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constant device configuration data structure.
|
||||
*
|
||||
* This struct contains all data of the PS GPIO controller parent
|
||||
* which is required for proper operation (such as base memory
|
||||
* addresses, references to all associated banks etc.) which don't
|
||||
* have to and therefore cannot be modified at run-time.
|
||||
*/
|
||||
struct gpio_xlnx_ps_dev_cfg {
|
||||
struct gpio_driver_config common;
|
||||
|
||||
uint32_t base_addr;
|
||||
const struct device **bank_devices;
|
||||
uint32_t num_banks;
|
||||
gpio_xlnx_ps_config_irq_t config_func;
|
||||
};
|
||||
|
||||
#endif /* _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_H_ */
|
447
drivers/gpio/gpio_xlnx_ps_bank.c
Normal file
447
drivers/gpio/gpio_xlnx_ps_bank.c
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* Xilinx Processor System MIO / EMIO GPIO controller driver
|
||||
* GPIO bank module
|
||||
*
|
||||
* Copyright (c) 2022, Weidmueller Interface GmbH & Co. KG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <devicetree.h>
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include "gpio_utils.h"
|
||||
#include "gpio_xlnx_ps_bank.h"
|
||||
|
||||
#define LOG_MODULE_NAME gpio_xlnx_ps_bank
|
||||
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#define DT_DRV_COMPAT xlnx_ps_gpio_bank
|
||||
|
||||
/**
|
||||
* @brief GPIO bank pin configuration function
|
||||
*
|
||||
* Configures an individual pin within a MIO / EMIO GPIO pin bank.
|
||||
* The following flags specified by the GPIO subsystem are NOT
|
||||
* supported by the PS GPIO controller:
|
||||
*
|
||||
* - Pull up
|
||||
* - Pull down
|
||||
* - Open drain
|
||||
* - Open source.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param pin Index of the pin within the bank to be configured
|
||||
* (decimal index of the pin).
|
||||
* @param flags Flags specifying the pin's configuration.
|
||||
*
|
||||
* @retval 0 if the device initialization completed successfully,
|
||||
* -EINVAL if the specified pin index is out of range,
|
||||
* -ENOTSUP if the pin configuration data contains a flag
|
||||
* that is not supported by the controller.
|
||||
*/
|
||||
static int gpio_xlnx_ps_pin_configure(const struct device *dev,
|
||||
gpio_pin_t pin,
|
||||
gpio_flags_t flags)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t pin_mask = BIT(pin);
|
||||
uint32_t bank_data;
|
||||
uint32_t dirm_data;
|
||||
uint32_t oen_data;
|
||||
|
||||
/* Validity of the specified pin index is checked in drivers/gpio.h */
|
||||
|
||||
/* Check for config flags not supported by the controller */
|
||||
if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_SINGLE_ENDED)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Read the data direction & output enable registers */
|
||||
dirm_data = sys_read32(GPIO_XLNX_PS_BANK_DIRM_REG);
|
||||
oen_data = sys_read32(GPIO_XLNX_PS_BANK_OEN_REG);
|
||||
|
||||
if (flags & GPIO_OUTPUT) {
|
||||
dirm_data |= pin_mask;
|
||||
oen_data |= pin_mask;
|
||||
|
||||
/*
|
||||
* Setting of an initial value (see below) requires the
|
||||
* direction register to be written *BEFORE* the data
|
||||
* register, otherwise, the value will not be applied!
|
||||
* The output enable bit can be set after the initial
|
||||
* value has been written.
|
||||
*/
|
||||
sys_write32(dirm_data, GPIO_XLNX_PS_BANK_DIRM_REG);
|
||||
|
||||
/*
|
||||
* If the current pin is to be configured as an output
|
||||
* pin, it is up to the caller to specify whether the
|
||||
* output's initial value shall be high or low.
|
||||
* -> Write the initial output value into the data register.
|
||||
*/
|
||||
bank_data = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
if (flags & GPIO_OUTPUT_INIT_HIGH) {
|
||||
bank_data |= pin_mask;
|
||||
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
|
||||
bank_data &= ~pin_mask;
|
||||
}
|
||||
sys_write32(bank_data, GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
|
||||
/* Set the pin's output enable bit */
|
||||
sys_write32(oen_data, GPIO_XLNX_PS_BANK_OEN_REG);
|
||||
} else {
|
||||
dirm_data &= ~pin_mask;
|
||||
oen_data &= ~pin_mask;
|
||||
|
||||
/*
|
||||
* Disable the output first in case of an O -> I
|
||||
* transition, then change the pin's direction.
|
||||
*/
|
||||
sys_write32(oen_data, GPIO_XLNX_PS_BANK_OEN_REG);
|
||||
sys_write32(dirm_data, GPIO_XLNX_PS_BANK_DIRM_REG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the current bit mask of the entire GPIO pin bank.
|
||||
*
|
||||
* Reads the current bit mask of the entire bank from the
|
||||
* read-only data register. This includes the current values
|
||||
* of not just all input pins, but both the input and output
|
||||
* pins within the bank.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param value Pointer to a variable of type gpio_port_value_t
|
||||
* to which the current bit mask read from the bank's
|
||||
* RO data register will be written to.
|
||||
*
|
||||
* @retval 0 if the read operation completed successfully,
|
||||
* -EINVAL if the pointer to the output variable is NULL.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_get(const struct device *dev,
|
||||
gpio_port_value_t *value)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
|
||||
*value = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Masked write of a bit mask for the entire GPIO pin bank.
|
||||
*
|
||||
* Performs a masked write operation on the data register of
|
||||
* the current GPIO pin bank. The mask is applied twice:
|
||||
* first, it is applied to the current contents of the bank's
|
||||
* RO data register, clearing any bits that are zeroes in the
|
||||
* mask (will not have any effect on input pins). Second, it
|
||||
* is applied to the data word to be written into the current
|
||||
* bank's data register. The masked data word read from the
|
||||
* RO data register and the masked data word provided by the
|
||||
* caller ar then OR'ed and written to the bank's data register.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param mask Mask to be applied to both the current contents
|
||||
* of the data register and the data word provided
|
||||
* by the caller.
|
||||
* @param value Value to be written to the current bank's data
|
||||
* register.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_set_masked(const struct device *dev,
|
||||
gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t bank_data;
|
||||
|
||||
bank_data = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
bank_data = (bank_data & ~mask) | (value & mask);
|
||||
sys_write32(bank_data, GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets bits in the data register of the GPIO pin bank.
|
||||
*
|
||||
* Sets bits in the data register of the current GPIO pin bank
|
||||
* as a read-modify-write operation. All bits set in the bit
|
||||
* mask provided by the caller are OR'ed into the current data
|
||||
* word of the bank. This operation has no effect on the values
|
||||
* associated with pins configured as inputs.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param pins Bit mask specifying which bits shall be set in
|
||||
* the data word of the current GPIO pin bank.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_set_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t bank_data;
|
||||
|
||||
bank_data = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
bank_data |= pins;
|
||||
sys_write32(bank_data, GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears bits in the data register of the GPIO pin bank.
|
||||
*
|
||||
* Clears bits in the data register of the current GPIO pin bank
|
||||
* as a read-modify-write operation. All bits set in the bit
|
||||
* mask provided by the caller are NAND'ed into the current data
|
||||
* word of the bank. This operation has no effect on the values
|
||||
* associated with pins configured as inputs.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param pins Bit mask specifying which bits shall be cleared
|
||||
* in the data word of the current GPIO pin bank.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_clear_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t bank_data;
|
||||
|
||||
bank_data = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
bank_data &= ~pins;
|
||||
sys_write32(bank_data, GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggles bits in the data register of the GPIO pin bank.
|
||||
*
|
||||
* Toggles bits in the data register of the current GPIO pin bank
|
||||
* as a read-modify-write operation. All bits set in the bit
|
||||
* mask provided by the caller are XOR'ed into the current data
|
||||
* word of the bank. This operation has no effect on the values
|
||||
* associated with pins configured as inputs.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param pins Bit mask specifying which bits shall be toggled
|
||||
* in the data word of the current GPIO pin bank.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_toggle_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t bank_data;
|
||||
|
||||
bank_data = sys_read32(GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
bank_data ^= pins;
|
||||
sys_write32(bank_data, GPIO_XLNX_PS_BANK_DATA_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures the interrupt behaviour of a pin within the
|
||||
* current GPIO bank.
|
||||
*
|
||||
* Configures the interrupt behaviour of a pin within the current
|
||||
* GPIO bank. If a pin is to be configured to trigger an interrupt,
|
||||
* the following modes are supported:
|
||||
*
|
||||
* - edge or level triggered,
|
||||
* - rising edge / high level or falling edge / low level,
|
||||
* - in edge mode only: trigger on both rising and falling edge.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param pin Index of the pin within the bank to be configured
|
||||
* (decimal index of the pin).
|
||||
* @param mode Mode configuration: edge, level or interrupt disabled.
|
||||
* @param trig Trigger condition configuration: high/low level or
|
||||
* rising/falling/both edge(s).
|
||||
*
|
||||
* @retval 0 if the interrupt configuration completed successfully,
|
||||
* -EINVAL if the specified pin index is out of range,
|
||||
* -ENOTSUP if the interrupt configuration data contains an
|
||||
* invalid combination of configuration flags.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_pin_irq_configure(const struct device *dev,
|
||||
gpio_pin_t pin,
|
||||
enum gpio_int_mode mode,
|
||||
enum gpio_int_trig trig)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t pin_mask = BIT(pin);
|
||||
uint32_t int_type_data;
|
||||
uint32_t int_polarity_data;
|
||||
uint32_t int_any_data;
|
||||
|
||||
/* Validity of the specified pin index is checked in drivers/gpio.h */
|
||||
|
||||
/* Disable the specified pin's interrupt before (re-)configuring it */
|
||||
sys_write32(pin_mask, GPIO_XLNX_PS_BANK_INT_DIS_REG);
|
||||
|
||||
int_type_data = sys_read32(GPIO_XLNX_PS_BANK_INT_TYPE_REG);
|
||||
int_polarity_data = sys_read32(GPIO_XLNX_PS_BANK_INT_POLARITY_REG);
|
||||
int_any_data = sys_read32(GPIO_XLNX_PS_BANK_INT_ANY_REG);
|
||||
|
||||
if (mode != GPIO_INT_MODE_DISABLED) {
|
||||
|
||||
if (mode == GPIO_INT_MODE_LEVEL) {
|
||||
int_type_data &= ~pin_mask;
|
||||
} else if (mode == GPIO_INT_MODE_EDGE) {
|
||||
int_type_data |= pin_mask;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (trig == GPIO_INT_TRIG_LOW) {
|
||||
int_any_data &= ~pin_mask;
|
||||
int_polarity_data &= ~pin_mask;
|
||||
} else if (trig == GPIO_INT_TRIG_HIGH) {
|
||||
int_any_data &= ~pin_mask;
|
||||
int_polarity_data |= pin_mask;
|
||||
} else if (trig == GPIO_INT_TRIG_BOTH) {
|
||||
if (mode == GPIO_INT_MODE_LEVEL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
int_any_data |= pin_mask;
|
||||
}
|
||||
|
||||
} else { /* mode == GPIO_INT_MODE_DISABLED */
|
||||
int_any_data &= ~pin_mask;
|
||||
int_polarity_data &= ~pin_mask;
|
||||
int_type_data &= ~pin_mask;
|
||||
}
|
||||
|
||||
sys_write32(int_any_data, GPIO_XLNX_PS_BANK_INT_ANY_REG);
|
||||
sys_write32(int_polarity_data, GPIO_XLNX_PS_BANK_INT_POLARITY_REG);
|
||||
sys_write32(int_type_data, GPIO_XLNX_PS_BANK_INT_TYPE_REG);
|
||||
|
||||
if (mode != GPIO_INT_MODE_DISABLED) {
|
||||
/* Clear potential stale pending bit before enabling interrupt */
|
||||
sys_write32(pin_mask, GPIO_XLNX_PS_BANK_INT_STAT_REG);
|
||||
sys_write32(pin_mask, GPIO_XLNX_PS_BANK_INT_EN_REG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the interrupt status of the current GPIO bank.
|
||||
*
|
||||
* Returns the interrupt status of the current GPIO bank, in the
|
||||
* form of a bit mask where each pin with a pending interrupt is
|
||||
* indicated. This information can either be used by the PM sub-
|
||||
* system or the parent controller device driver, which manages
|
||||
* the interrupt line of the entire PS GPIO controller, regardless
|
||||
* of how many bank sub-devices exist. As the current status is
|
||||
* read, it is automatically cleared. Callback triggering is handled
|
||||
* by the parent controller device.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
*
|
||||
* @retval A bit mask indicating for which pins within the bank
|
||||
* an interrupt is pending.
|
||||
*/
|
||||
static uint32_t gpio_xlnx_ps_bank_get_int_status(const struct device *dev)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
uint32_t int_status;
|
||||
|
||||
int_status = sys_read32(GPIO_XLNX_PS_BANK_INT_STAT_REG);
|
||||
if (int_status != 0) {
|
||||
sys_write32(int_status, GPIO_XLNX_PS_BANK_INT_STAT_REG);
|
||||
}
|
||||
|
||||
return int_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback management re-direction function.
|
||||
*
|
||||
* Re-directs any callback management calls relating to the current
|
||||
* GPIO bank to the GPIO sub-system. Comp. documentation of the
|
||||
* underlying sub-system's #gpio_manage_callback function.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
* @param callback Pointer to a GPIO callback struct.
|
||||
* @param set Callback set flag.
|
||||
*
|
||||
* @retval A bit mask indicating for which pins within the bank
|
||||
* an interrupt is pending.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_manage_callback(const struct device *dev,
|
||||
struct gpio_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
struct gpio_xlnx_ps_bank_dev_data *dev_data = dev->data;
|
||||
|
||||
return gpio_manage_callback(&dev_data->callbacks, callback, set);
|
||||
}
|
||||
|
||||
/* GPIO bank device driver API */
|
||||
static const struct gpio_driver_api gpio_xlnx_ps_bank_apis = {
|
||||
.pin_configure = gpio_xlnx_ps_pin_configure,
|
||||
.port_get_raw = gpio_xlnx_ps_bank_get,
|
||||
.port_set_masked_raw = gpio_xlnx_ps_bank_set_masked,
|
||||
.port_set_bits_raw = gpio_xlnx_ps_bank_set_bits,
|
||||
.port_clear_bits_raw = gpio_xlnx_ps_bank_clear_bits,
|
||||
.port_toggle_bits = gpio_xlnx_ps_bank_toggle_bits,
|
||||
.pin_interrupt_configure = gpio_xlnx_ps_bank_pin_irq_configure,
|
||||
.manage_callback = gpio_xlnx_ps_bank_manage_callback,
|
||||
.get_pending_int = gpio_xlnx_ps_bank_get_int_status
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize a MIO / EMIO GPIO bank sub-device
|
||||
*
|
||||
* Initialize a MIO / EMIO GPIO bank sub-device, which is a child
|
||||
* of the parent Xilinx PS GPIO controller device driver. This ini-
|
||||
* tialization function sets up a defined initial state for each
|
||||
* GPIO bank.
|
||||
*
|
||||
* @param dev Pointer to the GPIO bank's device.
|
||||
*
|
||||
* @retval Always 0.
|
||||
*/
|
||||
static int gpio_xlnx_ps_bank_init(const struct device *dev)
|
||||
{
|
||||
const struct gpio_xlnx_ps_bank_dev_cfg *dev_conf = dev->config;
|
||||
|
||||
sys_write32(~0x0, GPIO_XLNX_PS_BANK_INT_DIS_REG); /* Disable all interrupts */
|
||||
sys_write32(~0x0, GPIO_XLNX_PS_BANK_INT_STAT_REG); /* Clear all interrupts */
|
||||
sys_write32(0x0, GPIO_XLNX_PS_BANK_OEN_REG); /* All outputs disabled */
|
||||
sys_write32(0x0, GPIO_XLNX_PS_BANK_DIRM_REG); /* All pins input */
|
||||
sys_write32(0x0, GPIO_XLNX_PS_BANK_DATA_REG); /* Zero data register */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MIO / EMIO bank device definition macros */
|
||||
#define GPIO_XLNX_PS_BANK_INIT(idx)\
|
||||
static const struct gpio_xlnx_ps_bank_dev_cfg gpio_xlnx_ps_bank##idx##_cfg = {\
|
||||
.common = {\
|
||||
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx),\
|
||||
},\
|
||||
.base_addr = DT_REG_ADDR(DT_PARENT(DT_INST(idx, DT_DRV_COMPAT))),\
|
||||
.bank_index = idx,\
|
||||
};\
|
||||
static struct gpio_xlnx_ps_bank_dev_data gpio_xlnx_ps_bank##idx##_data;\
|
||||
DEVICE_DT_INST_DEFINE(idx, gpio_xlnx_ps_bank_init, NULL,\
|
||||
&gpio_xlnx_ps_bank##idx##_data, &gpio_xlnx_ps_bank##idx##_cfg,\
|
||||
PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, &gpio_xlnx_ps_bank_apis);
|
||||
|
||||
/* Register & initialize all MIO / EMIO GPIO banks specified in the device tree. */
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_XLNX_PS_BANK_INIT);
|
72
drivers/gpio/gpio_xlnx_ps_bank.h
Normal file
72
drivers/gpio/gpio_xlnx_ps_bank.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Xilinx Processor System MIO / EMIO GPIO controller driver
|
||||
*
|
||||
* Driver private data declarations, GPIO bank module
|
||||
*
|
||||
* Copyright (c) 2022, Weidmueller Interface GmbH & Co. KG
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_BANK_H_
|
||||
#define _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_BANK_H_
|
||||
|
||||
/*
|
||||
* Register address calculation macros
|
||||
* Register address offsets: comp. Zynq-7000 TRM, ug585, chap. B.19
|
||||
*/
|
||||
#define GPIO_XLNX_PS_BANK_MASK_DATA_LSW_REG (dev_conf->base_addr\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x8))
|
||||
#define GPIO_XLNX_PS_BANK_MASK_DATA_MSW_REG ((dev_conf->base_addr + 0x04)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x8))
|
||||
#define GPIO_XLNX_PS_BANK_DATA_REG ((dev_conf->base_addr + 0x40)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x4))
|
||||
#define GPIO_XLNX_PS_BANK_DATA_RO_REG ((dev_conf->base_addr + 0x60)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x4))
|
||||
#define GPIO_XLNX_PS_BANK_DIRM_REG ((dev_conf->base_addr + 0x204)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_OEN_REG ((dev_conf->base_addr + 0x208)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_MASK_REG ((dev_conf->base_addr + 0x20C)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_EN_REG ((dev_conf->base_addr + 0x210)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_DIS_REG ((dev_conf->base_addr + 0x214)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_STAT_REG ((dev_conf->base_addr + 0x218)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_TYPE_REG ((dev_conf->base_addr + 0x21C)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_POLARITY_REG ((dev_conf->base_addr + 0x220)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
#define GPIO_XLNX_PS_BANK_INT_ANY_REG ((dev_conf->base_addr + 0x224)\
|
||||
+ ((uint32_t)dev_conf->bank_index * 0x40))
|
||||
|
||||
/**
|
||||
* @brief Run-time modifiable device data structure.
|
||||
*
|
||||
* This struct contains all data of a PS MIO / EMIO GPIO bank
|
||||
* which is modifiable at run-time, such as the configuration
|
||||
* parameters and current values of each individual pin belonging
|
||||
* to the respective bank.
|
||||
*/
|
||||
struct gpio_xlnx_ps_bank_dev_data {
|
||||
struct gpio_driver_data common;
|
||||
sys_slist_t callbacks;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constant device configuration data structure.
|
||||
*
|
||||
* This struct contains all data of a PS MIO / EMIO GPIO bank
|
||||
* which is required for proper operation (such as base memory
|
||||
* addresses etc.) which don't have to and therefore cannot be
|
||||
* modified at run-time.
|
||||
*/
|
||||
struct gpio_xlnx_ps_bank_dev_cfg {
|
||||
struct gpio_driver_config common;
|
||||
|
||||
uint32_t base_addr;
|
||||
uint8_t bank_index;
|
||||
};
|
||||
|
||||
#endif /* _ZEPHYR_DRIVERS_GPIO_GPIO_XLNX_PS_BANK_H_ */
|
Loading…
Reference in a new issue