From 6cece5854499bfba8b00aa23588c78156fe1ff16 Mon Sep 17 00:00:00 2001 From: Christian Mauderer Date: Tue, 3 Nov 2020 11:21:41 +0100 Subject: bsps/imx: Move imx-gpio to arm/shared Update #4180 --- bsps/arm/imx/gpio/imx-gpio.c | 359 --------------------------------- bsps/arm/imx/headers.am | 5 - bsps/arm/imx/include/bsp/imx-gpio.h | 196 ------------------ bsps/arm/shared/headers.am | 5 + bsps/arm/shared/include/bsp/imx-gpio.h | 196 ++++++++++++++++++ bsps/arm/shared/pins/imx-gpio.c | 359 +++++++++++++++++++++++++++++++++ c/src/lib/libbsp/arm/imx/Makefile.am | 2 +- spec/build/bsps/arm/imx/bspimx.yml | 4 +- 8 files changed, 563 insertions(+), 563 deletions(-) delete mode 100644 bsps/arm/imx/gpio/imx-gpio.c delete mode 100644 bsps/arm/imx/include/bsp/imx-gpio.h create mode 100644 bsps/arm/shared/include/bsp/imx-gpio.h create mode 100644 bsps/arm/shared/pins/imx-gpio.c diff --git a/bsps/arm/imx/gpio/imx-gpio.c b/bsps/arm/imx/gpio/imx-gpio.c deleted file mode 100644 index 552e1d5cc2..0000000000 --- a/bsps/arm/imx/gpio/imx-gpio.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (C) 2019-2020 embedded brains GmbH. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define IMX_GPIO_ALIAS_NAME "gpioX" - -/* - * i.MX6ULL has 5, i.MX7D has 7 - * - * Be careful when changing this. The attach() does a simple ASCII conversion. - */ -#define IMX_MAX_GPIO_MODULES 7 - -struct imx_gpio_regs { - uint32_t dr; - uint32_t gdir; - uint32_t psr; - uint32_t icr1; -#define IMX_GPIO_ICR_LOW_LEVEL 0 -#define IMX_GPIO_ICR_HIGH_LEVEL 1 -#define IMX_GPIO_ICR_RISING_EDGE 2 -#define IMX_GPIO_ICR_FALLING_EDGE 3 - uint32_t icr2; - uint32_t imr; - uint32_t isr; - uint32_t edge_sel; -}; - -struct imx_gpio { - char name[sizeof(IMX_GPIO_ALIAS_NAME)]; - struct imx_gpio_regs *regs; - rtems_interrupt_lock lock; -}; - -/* The GPIO modules. These will be initialized based on the FDT alias table. */ -struct imx_gpio imx_gpio[IMX_MAX_GPIO_MODULES]; - -const char *imx_gpio_get_name(struct imx_gpio *imx_gpio) -{ - return imx_gpio->name; -} - -static void imx_gpio_attach(void) -{ - size_t i; - const void *fdt; - - fdt = bsp_fdt_get(); - - memset(imx_gpio, 0, sizeof(imx_gpio)); - - for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) { - const char *path; - int node; - const uint32_t *val; - uint32_t gpio_regs = 0; - int len; - - memcpy(imx_gpio[i].name, IMX_GPIO_ALIAS_NAME, sizeof(IMX_GPIO_ALIAS_NAME)); - imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-2] = (char)('0' + i); - - path = fdt_get_alias(fdt, imx_gpio[i].name); - if (path == NULL) { - continue; - } - - node = fdt_path_offset(fdt, path); - if (node < 0) { - bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT); - } - - val = fdt_getprop(fdt, node, "reg", &len); - if (len > 0) { - gpio_regs = fdt32_to_cpu(val[0]); - } else { - bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT); - } - - imx_gpio[i].regs = (struct imx_gpio_regs *)gpio_regs; - rtems_interrupt_lock_initialize(&imx_gpio[i].lock, imx_gpio[i].name); - } -} - -struct imx_gpio *imx_gpio_get_by_index(unsigned idx) -{ - if ((idx < IMX_MAX_GPIO_MODULES) && (imx_gpio[idx].regs != NULL)) { - return &imx_gpio[idx]; - } - return NULL; -} - -struct imx_gpio *imx_gpio_get_by_register(void *regs) -{ - size_t i; - - for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) { - if (imx_gpio[i].regs == regs) { - return &imx_gpio[i]; - } - } - return NULL; -} - -static void imx_gpio_direction_input(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->gdir &= ~pin->mask; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -static void imx_gpio_direction_output(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->gdir |= pin->mask; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -static void imx_gpio_set_interrupt_any_edge(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->edge_sel |= pin->mask; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -static void imx_gpio_set_interrupt_mode(struct imx_gpio_pin *pin, uint32_t mode) -{ - size_t i; - - for (i=0; i < 32; ++i) { - if ((pin->mask & (1u << i)) != 0) { - volatile uint32_t *icr; - size_t shift; - rtems_interrupt_lock_context lock_context; - - if (i < 16) { - icr = &pin->gpio->regs->icr1; - shift = 2 * i; - } else { - icr = &pin->gpio->regs->icr2; - shift = 2 * (i - 16); - } - - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - *icr = (*icr & ~(3u << shift)) | (mode << shift); - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); - } - } -} - -rtems_status_code imx_gpio_init_from_fdt_property ( - struct imx_gpio_pin *pin, - int node_offset, - const char *property, - enum imx_gpio_mode mode, - size_t index -) -{ - int len; - const uint32_t *val; - rtems_status_code sc = RTEMS_SUCCESSFUL; - const void *fdt; - uint32_t gpio_regs; - const unsigned pin_length_dwords = 3; - const unsigned pin_length_bytes = (pin_length_dwords * sizeof(uint32_t)); - uint32_t gpio_phandle; - uint32_t pin_nr; - int cfgnode; - - memset(pin, 0, sizeof(*pin)); - - fdt = bsp_fdt_get(); - val = fdt_getprop(fdt, node_offset, property, &len); - if (val == NULL || (len % pin_length_bytes != 0) || - (index >= len / pin_length_bytes)) { - sc = RTEMS_UNSATISFIED; - } - if (sc == RTEMS_SUCCESSFUL) { - pin_nr = fdt32_to_cpu(val[1 + index * pin_length_dwords]); - gpio_phandle = fdt32_to_cpu(val[0 + index * pin_length_dwords]); - - cfgnode = fdt_node_offset_by_phandle(fdt, gpio_phandle); - val = fdt_getprop(fdt, cfgnode, "reg", &len); - if (len > 0) { - gpio_regs = fdt32_to_cpu(val[0]); - } else { - sc = RTEMS_UNSATISFIED; - } - } - if (sc == RTEMS_SUCCESSFUL) { - pin->gpio = imx_gpio_get_by_register((void *)gpio_regs); - pin->mask = 1u << pin_nr; - pin->shift = pin_nr; - pin->mode = mode; - } - if (sc == RTEMS_SUCCESSFUL) { - imx_gpio_init(pin); - } - - return sc; -} - -rtems_vector_number imx_gpio_get_irq_of_node( - const void *fdt, - int node, - size_t index -) -{ - const uint32_t *val; - uint32_t pin; - int parent; - size_t parent_index; - int len; - - val = fdt_getprop(fdt, node, "interrupts", &len); - if (val == NULL || len < (int) ((index + 1) * 8)) { - return UINT32_MAX; - } - pin = fdt32_to_cpu(val[index * 2]); - if (pin < 16) { - parent_index = 0; - } else { - parent_index = 1; - } - - val = fdt_getprop(fdt, node, "interrupt-parent", &len); - if (len != 4) { - return UINT32_MAX; - } - parent = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[0])); - - return imx_get_irq_of_node(fdt, parent, parent_index); -} - -void imx_gpio_init (struct imx_gpio_pin *pin) -{ - switch (pin->mode) { - case (IMX_GPIO_MODE_INTERRUPT_LOW): - imx_gpio_direction_input(pin); - imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_LOW_LEVEL); - break; - case (IMX_GPIO_MODE_INTERRUPT_HIGH): - imx_gpio_direction_input(pin); - imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_HIGH_LEVEL); - break; - case (IMX_GPIO_MODE_INTERRUPT_RISING): - imx_gpio_direction_input(pin); - imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_RISING_EDGE); - break; - case (IMX_GPIO_MODE_INTERRUPT_FALLING): - imx_gpio_direction_input(pin); - imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE); - break; - case (IMX_GPIO_MODE_INTERRUPT_ANY_EDGE): - imx_gpio_direction_input(pin); - imx_gpio_set_interrupt_any_edge(pin); - /* Interrupt mode isn't really relevant here. Just set it to get - * a defined behaviour in case of a bug. */ - imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE); - break; - case (IMX_GPIO_MODE_INPUT): - imx_gpio_direction_input(pin); - break; - case (IMX_GPIO_MODE_OUTPUT): - imx_gpio_direction_output(pin); - break; - default: - assert(false); - break; - } -} - -void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set) -{ - rtems_interrupt_lock_context lock_context; - set <<= pin->shift; - set &= pin->mask; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->dr = (pin->gpio->regs->dr & ~pin->mask) | set; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -void imx_gpio_toggle_output(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->dr = (pin->gpio->regs->dr ^ pin->mask); - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin) -{ - return (pin->gpio->regs->dr & pin->mask) >> pin->shift; -} - -void imx_gpio_int_disable(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->imr &= ~pin->mask; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -void imx_gpio_int_enable(struct imx_gpio_pin *pin) -{ - rtems_interrupt_lock_context lock_context; - rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); - pin->gpio->regs->imr |= pin->mask; - rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); -} - -uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin) -{ - return (pin->gpio->regs->isr & pin->mask) >> pin->shift; -} - -void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr) -{ - pin->gpio->regs->isr = (clr << pin->shift) & pin->mask; -} - -RTEMS_SYSINIT_ITEM( - imx_gpio_attach, - RTEMS_SYSINIT_DEVICE_DRIVERS, - RTEMS_SYSINIT_ORDER_FIRST -); diff --git a/bsps/arm/imx/headers.am b/bsps/arm/imx/headers.am index e7a2418dea..4db8035c38 100644 --- a/bsps/arm/imx/headers.am +++ b/bsps/arm/imx/headers.am @@ -17,9 +17,4 @@ include_arm_freescale_imx_HEADERS += ../../../../../../bsps/arm/imx/include/arm/ include_bspdir = $(includedir)/bsp include_bsp_HEADERS = -include_bsp_HEADERS += ../../../../../../bsps/arm/imx/include/bsp/imx-gpio.h include_bsp_HEADERS += ../../../../../../bsps/arm/imx/include/bsp/irq.h - -include_dev_clockdir = $(includedir)/dev/clock -include_dev_clock_HEADERS = -include_dev_clock_HEADERS += ../../../../../../bsps/include/dev/clock/arm-generic-timer.h diff --git a/bsps/arm/imx/include/bsp/imx-gpio.h b/bsps/arm/imx/include/bsp/imx-gpio.h deleted file mode 100644 index dca2d0cfad..0000000000 --- a/bsps/arm/imx/include/bsp/imx-gpio.h +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ - -/* - * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef BSP_IMX_GPIO_H -#define BSP_IMX_GPIO_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** Hardware registers and locking mechanism for one hardware GPIO module. */ -struct imx_gpio; - -/** Mode of the pin. */ -enum imx_gpio_mode { - IMX_GPIO_MODE_OUTPUT, - IMX_GPIO_MODE_INPUT, - IMX_GPIO_MODE_INTERRUPT_LOW, - IMX_GPIO_MODE_INTERRUPT_HIGH, - IMX_GPIO_MODE_INTERRUPT_RISING, - IMX_GPIO_MODE_INTERRUPT_FALLING, - IMX_GPIO_MODE_INTERRUPT_ANY_EDGE, -}; - -/** - * A i.MX GPIO pin or set of pins. - * - * Use this structures to handle pins in the application. You can either get - * them from an FDT entry (with @ref imx_gpio_init_from_fde_property) or fill - * them by hand. - */ -struct imx_gpio_pin { - /** Management structure for the GPIO. Get with @ref imx_gpio_get_by_index. */ - volatile struct imx_gpio* gpio; - /** - * Select the pins you want to handle with this mask. The mask is not - * influenced by the @a shift field. - */ - uint32_t mask; - /** If set to something != 0: Shift the pins that many bits. */ - unsigned int shift; - /** Whether the pin is an input, output, interrupt, ... */ - enum imx_gpio_mode mode; -}; - -/** - * Initialize a GPIO pin. Only necessary for manually filled imx_gpio - * structures. - */ -void imx_gpio_init (struct imx_gpio_pin *pin); - -/** - * Initialize a GPIO pin from a FDT property. - * - * If you have for example the following property in an FDT node: - * - * some-node { - * gpios = <&gpio5 1 GPIO_ACTIVE_LOW>, <&gpio4 22 GPIO_ACTIVE_LOW>; - * }; - * - * you can use the following to initialize the second GPIO: - * - * imx_gpio_init_from_fdt_property(&pin, node, "gpios", - * IMX_GPIO_INTERRUPT_LOW, 1); - * - * NOTE: The information from the third parameter in the FDT (GPIO_ACTIVE_LOW in - * the example) is currently ignored. - */ -rtems_status_code imx_gpio_init_from_fdt_property( - struct imx_gpio_pin *pin, - int node_offset, - const char *property, - enum imx_gpio_mode mode, - size_t index); - -/** - * Return the RTEMS interrupt vector belonging to the GPIO interrupt of a given - * node. The node should look like follows: - * - * some-node { - * interrupt-parent = <&gpio4>; - * interrupts = <15 IRQ_TYPE_EDGE_BOTH>, <22 IRQ_TYPE_EDGE_BOTH>; - * }; - * - * To get the interrupt vector from the first GPIO in interrupts use - * - * imx_gpio_get_irq_of_node(fdt, node, 0); - * - * @returns the interrupt vector if successful. - * @returns BSP_INTERRUPT_VECTOR_INVALID on failure. - */ -rtems_vector_number imx_gpio_get_irq_of_node( - const void *fdt, - int node, - size_t index); - -/** - * Return the gpio management structure based on the GPIO index. The index is - * the one used in the FDT alias list. So index 0 is GPIO1 in the i.MX docs for - * most FDTs based on the Linux one. - */ -struct imx_gpio *imx_gpio_get_by_index(unsigned idx); - -/** - * Return the gpio management structure based on the GPIO registers. - */ -struct imx_gpio *imx_gpio_get_by_register(void *regs); - -/** - * Get the name of the gpio. - */ -const char *imx_gpio_get_name(struct imx_gpio *imx_gpio); - -/** - * Set the value of the output pin. @a set will be shifted and masked (in that - * order) based on the values of @a pin. - */ -void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set); - -/** - * Toggle the value of the output pin. - */ -void imx_gpio_toggle_output(struct imx_gpio_pin *pin); - -/** - * Get the value of the input pin. The input value will be masked and shifted - * (in that order) based on the values of @a pin. - */ -uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin); - -/** - * Disable the interrupt of the given @a pin. - */ -void imx_gpio_int_disable(struct imx_gpio_pin *pin); - -/** - * Enable the interrupt of the given @a pin. - */ -void imx_gpio_int_enable(struct imx_gpio_pin *pin); - -/** - * Read the interrupt status register for the given @a pin. - */ -uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin); - -/** - * Clear the interrupt status register for the given @a pin. - */ -void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr); - -/** - * Fast access macros for the GPIOs. Note that these assume a FDT based on the - * Linux FDTs. - */ -/** @{ */ -#define IMX_GPIO1 (imx_gpio_get_by_index(0)) -#define IMX_GPIO2 (imx_gpio_get_by_index(1)) -#define IMX_GPIO3 (imx_gpio_get_by_index(2)) -#define IMX_GPIO4 (imx_gpio_get_by_index(3)) -#define IMX_GPIO5 (imx_gpio_get_by_index(4)) -#define IMX_GPIO6 (imx_gpio_get_by_index(5)) -#define IMX_GPIO7 (imx_gpio_get_by_index(6)) -/** @} */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* BSP_IMX_GPIO_H */ diff --git a/bsps/arm/shared/headers.am b/bsps/arm/shared/headers.am index 4fd7238f81..34474400ec 100644 --- a/bsps/arm/shared/headers.am +++ b/bsps/arm/shared/headers.am @@ -4,3 +4,8 @@ include_arm_freescale_imxdir = $(includedir)/arm/freescale/imx include_arm_freescale_imx_HEADERS = include_arm_freescale_imx_HEADERS += ../../../../../../bsps/arm/shared/include/arm/freescale/imx/imx_iomuxreg.h include_arm_freescale_imx_HEADERS += ../../../../../../bsps/arm/shared/include/arm/freescale/imx/imx_iomuxvar.h + +include_bspdir = $(includedir)/bsp +include_bsp_HEADERS = +include_bsp_HEADERS += ../../../../../../bsps/arm/shared/include/bsp/imx-gpio.h +include_bsp_HEADERS += ../../../../../../bsps/arm/shared/include/bsp/imx-iomux.h diff --git a/bsps/arm/shared/include/bsp/imx-gpio.h b/bsps/arm/shared/include/bsp/imx-gpio.h new file mode 100644 index 0000000000..dca2d0cfad --- /dev/null +++ b/bsps/arm/shared/include/bsp/imx-gpio.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BSP_IMX_GPIO_H +#define BSP_IMX_GPIO_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Hardware registers and locking mechanism for one hardware GPIO module. */ +struct imx_gpio; + +/** Mode of the pin. */ +enum imx_gpio_mode { + IMX_GPIO_MODE_OUTPUT, + IMX_GPIO_MODE_INPUT, + IMX_GPIO_MODE_INTERRUPT_LOW, + IMX_GPIO_MODE_INTERRUPT_HIGH, + IMX_GPIO_MODE_INTERRUPT_RISING, + IMX_GPIO_MODE_INTERRUPT_FALLING, + IMX_GPIO_MODE_INTERRUPT_ANY_EDGE, +}; + +/** + * A i.MX GPIO pin or set of pins. + * + * Use this structures to handle pins in the application. You can either get + * them from an FDT entry (with @ref imx_gpio_init_from_fde_property) or fill + * them by hand. + */ +struct imx_gpio_pin { + /** Management structure for the GPIO. Get with @ref imx_gpio_get_by_index. */ + volatile struct imx_gpio* gpio; + /** + * Select the pins you want to handle with this mask. The mask is not + * influenced by the @a shift field. + */ + uint32_t mask; + /** If set to something != 0: Shift the pins that many bits. */ + unsigned int shift; + /** Whether the pin is an input, output, interrupt, ... */ + enum imx_gpio_mode mode; +}; + +/** + * Initialize a GPIO pin. Only necessary for manually filled imx_gpio + * structures. + */ +void imx_gpio_init (struct imx_gpio_pin *pin); + +/** + * Initialize a GPIO pin from a FDT property. + * + * If you have for example the following property in an FDT node: + * + * some-node { + * gpios = <&gpio5 1 GPIO_ACTIVE_LOW>, <&gpio4 22 GPIO_ACTIVE_LOW>; + * }; + * + * you can use the following to initialize the second GPIO: + * + * imx_gpio_init_from_fdt_property(&pin, node, "gpios", + * IMX_GPIO_INTERRUPT_LOW, 1); + * + * NOTE: The information from the third parameter in the FDT (GPIO_ACTIVE_LOW in + * the example) is currently ignored. + */ +rtems_status_code imx_gpio_init_from_fdt_property( + struct imx_gpio_pin *pin, + int node_offset, + const char *property, + enum imx_gpio_mode mode, + size_t index); + +/** + * Return the RTEMS interrupt vector belonging to the GPIO interrupt of a given + * node. The node should look like follows: + * + * some-node { + * interrupt-parent = <&gpio4>; + * interrupts = <15 IRQ_TYPE_EDGE_BOTH>, <22 IRQ_TYPE_EDGE_BOTH>; + * }; + * + * To get the interrupt vector from the first GPIO in interrupts use + * + * imx_gpio_get_irq_of_node(fdt, node, 0); + * + * @returns the interrupt vector if successful. + * @returns BSP_INTERRUPT_VECTOR_INVALID on failure. + */ +rtems_vector_number imx_gpio_get_irq_of_node( + const void *fdt, + int node, + size_t index); + +/** + * Return the gpio management structure based on the GPIO index. The index is + * the one used in the FDT alias list. So index 0 is GPIO1 in the i.MX docs for + * most FDTs based on the Linux one. + */ +struct imx_gpio *imx_gpio_get_by_index(unsigned idx); + +/** + * Return the gpio management structure based on the GPIO registers. + */ +struct imx_gpio *imx_gpio_get_by_register(void *regs); + +/** + * Get the name of the gpio. + */ +const char *imx_gpio_get_name(struct imx_gpio *imx_gpio); + +/** + * Set the value of the output pin. @a set will be shifted and masked (in that + * order) based on the values of @a pin. + */ +void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set); + +/** + * Toggle the value of the output pin. + */ +void imx_gpio_toggle_output(struct imx_gpio_pin *pin); + +/** + * Get the value of the input pin. The input value will be masked and shifted + * (in that order) based on the values of @a pin. + */ +uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin); + +/** + * Disable the interrupt of the given @a pin. + */ +void imx_gpio_int_disable(struct imx_gpio_pin *pin); + +/** + * Enable the interrupt of the given @a pin. + */ +void imx_gpio_int_enable(struct imx_gpio_pin *pin); + +/** + * Read the interrupt status register for the given @a pin. + */ +uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin); + +/** + * Clear the interrupt status register for the given @a pin. + */ +void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr); + +/** + * Fast access macros for the GPIOs. Note that these assume a FDT based on the + * Linux FDTs. + */ +/** @{ */ +#define IMX_GPIO1 (imx_gpio_get_by_index(0)) +#define IMX_GPIO2 (imx_gpio_get_by_index(1)) +#define IMX_GPIO3 (imx_gpio_get_by_index(2)) +#define IMX_GPIO4 (imx_gpio_get_by_index(3)) +#define IMX_GPIO5 (imx_gpio_get_by_index(4)) +#define IMX_GPIO6 (imx_gpio_get_by_index(5)) +#define IMX_GPIO7 (imx_gpio_get_by_index(6)) +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* BSP_IMX_GPIO_H */ diff --git a/bsps/arm/shared/pins/imx-gpio.c b/bsps/arm/shared/pins/imx-gpio.c new file mode 100644 index 0000000000..552e1d5cc2 --- /dev/null +++ b/bsps/arm/shared/pins/imx-gpio.c @@ -0,0 +1,359 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2019-2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IMX_GPIO_ALIAS_NAME "gpioX" + +/* + * i.MX6ULL has 5, i.MX7D has 7 + * + * Be careful when changing this. The attach() does a simple ASCII conversion. + */ +#define IMX_MAX_GPIO_MODULES 7 + +struct imx_gpio_regs { + uint32_t dr; + uint32_t gdir; + uint32_t psr; + uint32_t icr1; +#define IMX_GPIO_ICR_LOW_LEVEL 0 +#define IMX_GPIO_ICR_HIGH_LEVEL 1 +#define IMX_GPIO_ICR_RISING_EDGE 2 +#define IMX_GPIO_ICR_FALLING_EDGE 3 + uint32_t icr2; + uint32_t imr; + uint32_t isr; + uint32_t edge_sel; +}; + +struct imx_gpio { + char name[sizeof(IMX_GPIO_ALIAS_NAME)]; + struct imx_gpio_regs *regs; + rtems_interrupt_lock lock; +}; + +/* The GPIO modules. These will be initialized based on the FDT alias table. */ +struct imx_gpio imx_gpio[IMX_MAX_GPIO_MODULES]; + +const char *imx_gpio_get_name(struct imx_gpio *imx_gpio) +{ + return imx_gpio->name; +} + +static void imx_gpio_attach(void) +{ + size_t i; + const void *fdt; + + fdt = bsp_fdt_get(); + + memset(imx_gpio, 0, sizeof(imx_gpio)); + + for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) { + const char *path; + int node; + const uint32_t *val; + uint32_t gpio_regs = 0; + int len; + + memcpy(imx_gpio[i].name, IMX_GPIO_ALIAS_NAME, sizeof(IMX_GPIO_ALIAS_NAME)); + imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-2] = (char)('0' + i); + + path = fdt_get_alias(fdt, imx_gpio[i].name); + if (path == NULL) { + continue; + } + + node = fdt_path_offset(fdt, path); + if (node < 0) { + bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT); + } + + val = fdt_getprop(fdt, node, "reg", &len); + if (len > 0) { + gpio_regs = fdt32_to_cpu(val[0]); + } else { + bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT); + } + + imx_gpio[i].regs = (struct imx_gpio_regs *)gpio_regs; + rtems_interrupt_lock_initialize(&imx_gpio[i].lock, imx_gpio[i].name); + } +} + +struct imx_gpio *imx_gpio_get_by_index(unsigned idx) +{ + if ((idx < IMX_MAX_GPIO_MODULES) && (imx_gpio[idx].regs != NULL)) { + return &imx_gpio[idx]; + } + return NULL; +} + +struct imx_gpio *imx_gpio_get_by_register(void *regs) +{ + size_t i; + + for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) { + if (imx_gpio[i].regs == regs) { + return &imx_gpio[i]; + } + } + return NULL; +} + +static void imx_gpio_direction_input(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->gdir &= ~pin->mask; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +static void imx_gpio_direction_output(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->gdir |= pin->mask; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +static void imx_gpio_set_interrupt_any_edge(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->edge_sel |= pin->mask; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +static void imx_gpio_set_interrupt_mode(struct imx_gpio_pin *pin, uint32_t mode) +{ + size_t i; + + for (i=0; i < 32; ++i) { + if ((pin->mask & (1u << i)) != 0) { + volatile uint32_t *icr; + size_t shift; + rtems_interrupt_lock_context lock_context; + + if (i < 16) { + icr = &pin->gpio->regs->icr1; + shift = 2 * i; + } else { + icr = &pin->gpio->regs->icr2; + shift = 2 * (i - 16); + } + + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + *icr = (*icr & ~(3u << shift)) | (mode << shift); + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); + } + } +} + +rtems_status_code imx_gpio_init_from_fdt_property ( + struct imx_gpio_pin *pin, + int node_offset, + const char *property, + enum imx_gpio_mode mode, + size_t index +) +{ + int len; + const uint32_t *val; + rtems_status_code sc = RTEMS_SUCCESSFUL; + const void *fdt; + uint32_t gpio_regs; + const unsigned pin_length_dwords = 3; + const unsigned pin_length_bytes = (pin_length_dwords * sizeof(uint32_t)); + uint32_t gpio_phandle; + uint32_t pin_nr; + int cfgnode; + + memset(pin, 0, sizeof(*pin)); + + fdt = bsp_fdt_get(); + val = fdt_getprop(fdt, node_offset, property, &len); + if (val == NULL || (len % pin_length_bytes != 0) || + (index >= len / pin_length_bytes)) { + sc = RTEMS_UNSATISFIED; + } + if (sc == RTEMS_SUCCESSFUL) { + pin_nr = fdt32_to_cpu(val[1 + index * pin_length_dwords]); + gpio_phandle = fdt32_to_cpu(val[0 + index * pin_length_dwords]); + + cfgnode = fdt_node_offset_by_phandle(fdt, gpio_phandle); + val = fdt_getprop(fdt, cfgnode, "reg", &len); + if (len > 0) { + gpio_regs = fdt32_to_cpu(val[0]); + } else { + sc = RTEMS_UNSATISFIED; + } + } + if (sc == RTEMS_SUCCESSFUL) { + pin->gpio = imx_gpio_get_by_register((void *)gpio_regs); + pin->mask = 1u << pin_nr; + pin->shift = pin_nr; + pin->mode = mode; + } + if (sc == RTEMS_SUCCESSFUL) { + imx_gpio_init(pin); + } + + return sc; +} + +rtems_vector_number imx_gpio_get_irq_of_node( + const void *fdt, + int node, + size_t index +) +{ + const uint32_t *val; + uint32_t pin; + int parent; + size_t parent_index; + int len; + + val = fdt_getprop(fdt, node, "interrupts", &len); + if (val == NULL || len < (int) ((index + 1) * 8)) { + return UINT32_MAX; + } + pin = fdt32_to_cpu(val[index * 2]); + if (pin < 16) { + parent_index = 0; + } else { + parent_index = 1; + } + + val = fdt_getprop(fdt, node, "interrupt-parent", &len); + if (len != 4) { + return UINT32_MAX; + } + parent = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[0])); + + return imx_get_irq_of_node(fdt, parent, parent_index); +} + +void imx_gpio_init (struct imx_gpio_pin *pin) +{ + switch (pin->mode) { + case (IMX_GPIO_MODE_INTERRUPT_LOW): + imx_gpio_direction_input(pin); + imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_LOW_LEVEL); + break; + case (IMX_GPIO_MODE_INTERRUPT_HIGH): + imx_gpio_direction_input(pin); + imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_HIGH_LEVEL); + break; + case (IMX_GPIO_MODE_INTERRUPT_RISING): + imx_gpio_direction_input(pin); + imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_RISING_EDGE); + break; + case (IMX_GPIO_MODE_INTERRUPT_FALLING): + imx_gpio_direction_input(pin); + imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE); + break; + case (IMX_GPIO_MODE_INTERRUPT_ANY_EDGE): + imx_gpio_direction_input(pin); + imx_gpio_set_interrupt_any_edge(pin); + /* Interrupt mode isn't really relevant here. Just set it to get + * a defined behaviour in case of a bug. */ + imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE); + break; + case (IMX_GPIO_MODE_INPUT): + imx_gpio_direction_input(pin); + break; + case (IMX_GPIO_MODE_OUTPUT): + imx_gpio_direction_output(pin); + break; + default: + assert(false); + break; + } +} + +void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set) +{ + rtems_interrupt_lock_context lock_context; + set <<= pin->shift; + set &= pin->mask; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->dr = (pin->gpio->regs->dr & ~pin->mask) | set; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +void imx_gpio_toggle_output(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->dr = (pin->gpio->regs->dr ^ pin->mask); + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin) +{ + return (pin->gpio->regs->dr & pin->mask) >> pin->shift; +} + +void imx_gpio_int_disable(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->imr &= ~pin->mask; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +void imx_gpio_int_enable(struct imx_gpio_pin *pin) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context); + pin->gpio->regs->imr |= pin->mask; + rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context); +} + +uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin) +{ + return (pin->gpio->regs->isr & pin->mask) >> pin->shift; +} + +void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr) +{ + pin->gpio->regs->isr = (clr << pin->shift) & pin->mask; +} + +RTEMS_SYSINIT_ITEM( + imx_gpio_attach, + RTEMS_SYSINIT_DEVICE_DRIVERS, + RTEMS_SYSINIT_ORDER_FIRST +); diff --git a/c/src/lib/libbsp/arm/imx/Makefile.am b/c/src/lib/libbsp/arm/imx/Makefile.am index 80871a286c..c9537a773d 100644 --- a/c/src/lib/libbsp/arm/imx/Makefile.am +++ b/c/src/lib/libbsp/arm/imx/Makefile.am @@ -66,7 +66,7 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/cache/cache-cp15.c librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/cache/cache-v7ar-disable-data.S # GPIO -librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/imx/gpio/imx-gpio.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/pins/imx-gpio.c # I2C librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/imx/i2c/imx-i2c.c diff --git a/spec/build/bsps/arm/imx/bspimx.yml b/spec/build/bsps/arm/imx/bspimx.yml index 442c631b3a..6b3cfdbb2e 100644 --- a/spec/build/bsps/arm/imx/bspimx.yml +++ b/spec/build/bsps/arm/imx/bspimx.yml @@ -28,7 +28,7 @@ install: - bsps/arm/imx/include/arm/freescale/imx/imx_wdogreg.h - destination: ${BSP_INCLUDEDIR}/bsp source: - - bsps/arm/imx/include/bsp/imx-gpio.h + - bsps/arm/shared/include/bsp/imx-gpio.h - bsps/arm/imx/include/bsp/irq.h - bsps/arm/shared/include/bsp/imx-iomux.h - destination: ${BSP_INCLUDEDIR}/dev/clock @@ -83,7 +83,7 @@ links: uid: ../../bspopts source: - bsps/arm/imx/console/console-config.c -- bsps/arm/imx/gpio/imx-gpio.c +- bsps/arm/shared/pins/imx-gpio.c - bsps/arm/imx/i2c/imx-i2c.c - bsps/arm/imx/spi/imx-ecspi.c - bsps/arm/imx/start/bspreset.c -- cgit v1.2.3