Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:05

0001 /*
0002  * SPDX-License-Identifier: BSD-2-Clause
0003  *
0004  * Copyright (C) 2019, 2020 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <assert.h>
0029 #include <bsp/fatal.h>
0030 #include <bsp/fdt.h>
0031 #include <bsp/imx-gpio.h>
0032 #include <libfdt.h>
0033 #include <rtems.h>
0034 #include <rtems/sysinit.h>
0035 
0036 /*
0037  * Define active levels like they are used in Linux Device Tree files.
0038  */
0039 #define GPIO_ACTIVE_HIGH 0
0040 #define GPIO_ACTIVE_LOW 1
0041 
0042 /*
0043  * Most of the time it's gpio1 or gpio13.
0044  */
0045 #define IMX_GPIO_ALIAS_NAME "gpioXY"
0046 
0047 /*
0048  * i.MX6ULL has 5, i.MX7D has 7, i.MXRT1160 has 13 (base) + 2 (core-specific).
0049  *
0050  * Be careful when changing this. The attach() does a simple ASCII conversion.
0051  */
0052 #define IMX_MAX_GPIO_MODULES 15
0053 
0054 struct imx_gpio_regs {
0055   uint32_t dr;
0056   uint32_t gdir;
0057   uint32_t psr;
0058   uint32_t icr1;
0059 #define IMX_GPIO_ICR_LOW_LEVEL 0
0060 #define IMX_GPIO_ICR_HIGH_LEVEL 1
0061 #define IMX_GPIO_ICR_RISING_EDGE 2
0062 #define IMX_GPIO_ICR_FALLING_EDGE 3
0063   uint32_t icr2;
0064   uint32_t imr;
0065   uint32_t isr;
0066   uint32_t edge_sel;
0067 };
0068 
0069 struct imx_gpio {
0070   char name[sizeof(IMX_GPIO_ALIAS_NAME)];
0071   struct imx_gpio_regs *regs;
0072   rtems_interrupt_lock lock;
0073 };
0074 
0075 /* The GPIO modules. These will be initialized based on the FDT alias table. */
0076 struct imx_gpio imx_gpio[IMX_MAX_GPIO_MODULES];
0077 
0078 const char *imx_gpio_get_name(struct imx_gpio *imx_gpio)
0079 {
0080   return imx_gpio->name;
0081 }
0082 
0083 static void imx_gpio_attach(void)
0084 {
0085   size_t i;
0086   const void *fdt;
0087 
0088   fdt = bsp_fdt_get();
0089 
0090   memset(imx_gpio, 0, sizeof(imx_gpio));
0091 
0092   for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) {
0093     const char *path;
0094     int node;
0095     const uint32_t *val;
0096     uint32_t gpio_regs = 0;
0097     int len;
0098 
0099     memcpy(imx_gpio[i].name, IMX_GPIO_ALIAS_NAME, sizeof(IMX_GPIO_ALIAS_NAME));
0100     if (i < 10) {
0101       imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-3] = (char)('0' + i);
0102       imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-2] = '\0';
0103     } else {
0104       imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-3] = (char)('0' + i / 10);
0105       imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-2] = (char)('0' + i % 10);
0106       imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-1] = '\0';
0107     }
0108 
0109     path = fdt_get_alias(fdt, imx_gpio[i].name);
0110     if (path == NULL) {
0111       continue;
0112     }
0113 
0114     node = fdt_path_offset(fdt, path);
0115     if (node < 0) {
0116       bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT);
0117     }
0118 
0119     val = fdt_getprop(fdt, node, "reg", &len);
0120     if (len > 0) {
0121       gpio_regs = fdt32_to_cpu(val[0]);
0122     } else {
0123       bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT);
0124     }
0125 
0126     imx_gpio[i].regs = (struct imx_gpio_regs *)gpio_regs;
0127     rtems_interrupt_lock_initialize(&imx_gpio[i].lock, imx_gpio[i].name);
0128   }
0129 }
0130 
0131 struct imx_gpio *imx_gpio_get_by_index(unsigned idx)
0132 {
0133   if ((idx < IMX_MAX_GPIO_MODULES) && (imx_gpio[idx].regs != NULL)) {
0134     return &imx_gpio[idx];
0135   }
0136   return NULL;
0137 }
0138 
0139 struct imx_gpio *imx_gpio_get_by_register(void *regs)
0140 {
0141   size_t i;
0142 
0143   for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) {
0144     if (imx_gpio[i].regs == regs) {
0145       return &imx_gpio[i];
0146     }
0147   }
0148   return NULL;
0149 }
0150 
0151 static void imx_gpio_direction_input(struct imx_gpio_pin *pin)
0152 {
0153   rtems_interrupt_lock_context lock_context;
0154   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0155   pin->gpio->regs->gdir &= ~pin->mask;
0156   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0157 }
0158 
0159 static void imx_gpio_direction_output(struct imx_gpio_pin *pin)
0160 {
0161   rtems_interrupt_lock_context lock_context;
0162   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0163   pin->gpio->regs->gdir |= pin->mask;
0164   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0165 }
0166 
0167 static void imx_gpio_set_interrupt_any_edge(struct imx_gpio_pin *pin)
0168 {
0169   rtems_interrupt_lock_context lock_context;
0170   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0171   pin->gpio->regs->edge_sel |= pin->mask;
0172   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0173 }
0174 
0175 static void imx_gpio_set_interrupt_mode(struct imx_gpio_pin *pin, uint32_t mode)
0176 {
0177   size_t i;
0178 
0179   for (i=0; i < 32; ++i) {
0180     if ((pin->mask & (1u << i)) != 0) {
0181       volatile uint32_t *icr;
0182       size_t shift;
0183       rtems_interrupt_lock_context lock_context;
0184 
0185       if (i < 16) {
0186         icr = &pin->gpio->regs->icr1;
0187         shift = 2 * i;
0188       } else {
0189         icr = &pin->gpio->regs->icr2;
0190         shift = 2 * (i - 16);
0191       }
0192 
0193       rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0194       *icr = (*icr & ~(3u << shift)) | (mode << shift);
0195       rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0196     }
0197   }
0198 }
0199 
0200 rtems_status_code imx_gpio_init_from_fdt_property_pointer (
0201   struct imx_gpio_pin *pin,
0202   const uint32_t *prop_pointer,
0203   enum imx_gpio_mode mode,
0204   const uint32_t **next_prop_pointer
0205 )
0206 {
0207   int len;
0208   const uint32_t *val;
0209   rtems_status_code sc = RTEMS_SUCCESSFUL;
0210   const void *fdt;
0211   uint32_t gpio_regs;
0212   const unsigned pin_length_dwords = 3;
0213   uint32_t gpio_phandle;
0214   uint32_t pin_nr;
0215   uint32_t active_level;
0216   int cfgnode;
0217 
0218   memset(pin, 0, sizeof(*pin));
0219 
0220   fdt = bsp_fdt_get();
0221   if (sc == RTEMS_SUCCESSFUL) {
0222     pin_nr = fdt32_to_cpu(prop_pointer[1]);
0223     gpio_phandle = fdt32_to_cpu(prop_pointer[0]);
0224     active_level = fdt32_to_cpu(prop_pointer[2]);
0225 
0226     cfgnode = fdt_node_offset_by_phandle(fdt, gpio_phandle);
0227     /* FIXME: Check compatible strings here. */
0228     val = fdt_getprop(fdt, cfgnode, "reg", &len);
0229     if (len > 0) {
0230       gpio_regs = fdt32_to_cpu(val[0]);
0231     } else {
0232       sc = RTEMS_UNSATISFIED;
0233     }
0234   }
0235   if (sc == RTEMS_SUCCESSFUL) {
0236     pin->gpio = imx_gpio_get_by_register((void *)gpio_regs);
0237     pin->mask = 1u << pin_nr;
0238     pin->shift = pin_nr;
0239     pin->mode = mode;
0240     pin->is_active_low = (active_level == GPIO_ACTIVE_LOW);
0241   }
0242   if (sc == RTEMS_SUCCESSFUL) {
0243     imx_gpio_init(pin);
0244   }
0245   if (sc == RTEMS_SUCCESSFUL && next_prop_pointer != NULL) {
0246     *next_prop_pointer = prop_pointer + pin_length_dwords;
0247   }
0248 
0249   return sc;
0250 }
0251 
0252 rtems_status_code imx_gpio_init_from_fdt_property (
0253   struct imx_gpio_pin *pin,
0254   int node_offset,
0255   const char *property,
0256   enum imx_gpio_mode mode,
0257   size_t index
0258 )
0259 {
0260   int len;
0261   const uint32_t *val;
0262   rtems_status_code sc = RTEMS_SUCCESSFUL;
0263   const void *fdt;
0264   const unsigned pin_length_dwords = 3;
0265   const unsigned pin_length_bytes = pin_length_dwords * 4;
0266 
0267   memset(pin, 0, sizeof(*pin));
0268 
0269   fdt = bsp_fdt_get();
0270   val = fdt_getprop(fdt, node_offset, property, &len);
0271   if (val == NULL || (len % pin_length_bytes != 0) ||
0272       (index >= len / pin_length_bytes)) {
0273     sc = RTEMS_UNSATISFIED;
0274   }
0275   if (sc == RTEMS_SUCCESSFUL) {
0276     sc = imx_gpio_init_from_fdt_property_pointer(
0277       pin,
0278       val + index * pin_length_dwords,
0279       mode,
0280       NULL);
0281   }
0282 
0283   return sc;
0284 }
0285 
0286 rtems_vector_number imx_gpio_get_irq_of_node(
0287   const void *fdt,
0288   int node,
0289   size_t index
0290 )
0291 {
0292   const uint32_t *val;
0293   uint32_t pin;
0294   int parent;
0295   size_t parent_index;
0296   int len;
0297 
0298   val = fdt_getprop(fdt, node, "interrupts", &len);
0299   if (val == NULL || len < (int) ((index + 1) * 8)) {
0300     return UINT32_MAX;
0301   }
0302   pin = fdt32_to_cpu(val[index * 2]);
0303   if (pin < 16) {
0304     parent_index = 0;
0305   } else {
0306     parent_index = 1;
0307   }
0308 
0309   val = fdt_getprop(fdt, node, "interrupt-parent", &len);
0310   if (len != 4) {
0311     return UINT32_MAX;
0312   }
0313   parent = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[0]));
0314 
0315   return imx_get_irq_of_node(fdt, parent, parent_index);
0316 }
0317 
0318 void imx_gpio_init (struct imx_gpio_pin *pin)
0319 {
0320   switch (pin->mode) {
0321   case (IMX_GPIO_MODE_INTERRUPT_LOW):
0322     imx_gpio_direction_input(pin);
0323     imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_LOW_LEVEL);
0324     break;
0325   case (IMX_GPIO_MODE_INTERRUPT_HIGH):
0326     imx_gpio_direction_input(pin);
0327     imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_HIGH_LEVEL);
0328     break;
0329   case (IMX_GPIO_MODE_INTERRUPT_RISING):
0330     imx_gpio_direction_input(pin);
0331     imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_RISING_EDGE);
0332     break;
0333   case (IMX_GPIO_MODE_INTERRUPT_FALLING):
0334     imx_gpio_direction_input(pin);
0335     imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE);
0336     break;
0337   case (IMX_GPIO_MODE_INTERRUPT_ANY_EDGE):
0338     imx_gpio_direction_input(pin);
0339     imx_gpio_set_interrupt_any_edge(pin);
0340     /* Interrupt mode isn't really relevant here. Just set it to get
0341      * a defined behaviour in case of a bug. */
0342     imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE);
0343     break;
0344   case (IMX_GPIO_MODE_INPUT):
0345     imx_gpio_direction_input(pin);
0346     break;
0347   case (IMX_GPIO_MODE_OUTPUT):
0348     imx_gpio_direction_output(pin);
0349     break;
0350   default:
0351     assert(false);
0352     break;
0353   }
0354 }
0355 
0356 void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set)
0357 {
0358   rtems_interrupt_lock_context lock_context;
0359   set <<= pin->shift;
0360   set &= pin->mask;
0361   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0362   pin->gpio->regs->dr = (pin->gpio->regs->dr & ~pin->mask) | set;
0363   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0364 }
0365 
0366 void imx_gpio_toggle_output(struct imx_gpio_pin *pin)
0367 {
0368   rtems_interrupt_lock_context lock_context;
0369   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0370   pin->gpio->regs->dr = (pin->gpio->regs->dr ^ pin->mask);
0371   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0372 }
0373 
0374 uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin)
0375 {
0376   return (pin->gpio->regs->dr & pin->mask) >> pin->shift;
0377 }
0378 
0379 void imx_gpio_int_disable(struct imx_gpio_pin *pin)
0380 {
0381   rtems_interrupt_lock_context lock_context;
0382   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0383   pin->gpio->regs->imr &= ~pin->mask;
0384   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0385 }
0386 
0387 void imx_gpio_int_enable(struct imx_gpio_pin *pin)
0388 {
0389   rtems_interrupt_lock_context lock_context;
0390   rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
0391   pin->gpio->regs->imr |= pin->mask;
0392   rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
0393 }
0394 
0395 uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin)
0396 {
0397   return (pin->gpio->regs->isr & pin->mask) >> pin->shift;
0398 }
0399 
0400 void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr)
0401 {
0402   pin->gpio->regs->isr = (clr << pin->shift) & pin->mask;
0403 }
0404 
0405 RTEMS_SYSINIT_ITEM(
0406   imx_gpio_attach,
0407   RTEMS_SYSINIT_DEVICE_DRIVERS,
0408   RTEMS_SYSINIT_ORDER_FIRST
0409 );