source: rtems/bsps/arm/imx/gpio/imx-gpio.c @ 0f4b911c

Last change on this file since 0f4b911c was 0f4b911c, checked in by Christian Mauderer <christian.mauderer@…>, on 07/17/20 at 05:54:43

bsp/imx: Add a GPIO driver

Update 3869

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2019-2020 embedded brains GmbH.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <assert.h>
29#include <bsp/fatal.h>
30#include <bsp/fdt.h>
31#include <bsp/imx-gpio.h>
32#include <libfdt.h>
33#include <rtems.h>
34#include <rtems/sysinit.h>
35
36#define IMX_GPIO_ALIAS_NAME "gpioX"
37
38/*
39 * i.MX6ULL has 5, i.MX7D has 7
40 *
41 * Be careful when changing this. The attach() does a simple ASCII conversion.
42 */
43#define IMX_MAX_GPIO_MODULES 7
44
45struct imx_gpio_regs {
46  uint32_t dr;
47  uint32_t gdir;
48  uint32_t psr;
49  uint32_t icr1;
50#define IMX_GPIO_ICR_LOW_LEVEL 0
51#define IMX_GPIO_ICR_HIGH_LEVEL 1
52#define IMX_GPIO_ICR_RISING_EDGE 2
53#define IMX_GPIO_ICR_FALLING_EDGE 3
54  uint32_t icr2;
55  uint32_t imr;
56  uint32_t isr;
57  uint32_t edge_sel;
58};
59
60struct imx_gpio {
61  char name[sizeof(IMX_GPIO_ALIAS_NAME)];
62  struct imx_gpio_regs *regs;
63  rtems_interrupt_lock lock;
64};
65
66/* The GPIO modules. These will be initialized based on the FDT alias table. */
67struct imx_gpio imx_gpio[IMX_MAX_GPIO_MODULES];
68
69const char *imx_gpio_get_name(struct imx_gpio *imx_gpio)
70{
71  return imx_gpio->name;
72}
73
74static void imx_gpio_attach(void)
75{
76  size_t i;
77  const void *fdt;
78
79  fdt = bsp_fdt_get();
80
81  memset(imx_gpio, 0, sizeof(imx_gpio));
82
83  for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) {
84    const char *path;
85    int node;
86    const uint32_t *val;
87    uint32_t gpio_regs = 0;
88    int len;
89
90    memcpy(imx_gpio[i].name, IMX_GPIO_ALIAS_NAME, sizeof(IMX_GPIO_ALIAS_NAME));
91    imx_gpio[i].name[sizeof(IMX_GPIO_ALIAS_NAME)-2] = (char)('0' + i);
92
93    path = fdt_get_alias(fdt, imx_gpio[i].name);
94    if (path == NULL) {
95      continue;
96    }
97
98    node = fdt_path_offset(fdt, path);
99    if (node < 0) {
100      bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT);
101    }
102
103    val = fdt_getprop(fdt, node, "reg", &len);
104    if (len > 0) {
105      gpio_regs = fdt32_to_cpu(val[0]);
106    } else {
107      bsp_fatal(IMX_FATAL_GPIO_UNEXPECTED_FDT);
108    }
109
110    imx_gpio[i].regs = (struct imx_gpio_regs *)gpio_regs;
111    rtems_interrupt_lock_initialize(&imx_gpio[i].lock, imx_gpio[i].name);
112  }
113}
114
115struct imx_gpio *imx_gpio_get_by_index(unsigned idx)
116{
117  if ((idx < IMX_MAX_GPIO_MODULES) && (imx_gpio[idx].regs != NULL)) {
118    return &imx_gpio[idx];
119  }
120  return NULL;
121}
122
123struct imx_gpio *imx_gpio_get_by_register(void *regs)
124{
125  size_t i;
126
127  for (i = 0; i < IMX_MAX_GPIO_MODULES; ++i) {
128    if (imx_gpio[i].regs == regs) {
129      return &imx_gpio[i];
130    }
131  }
132  return NULL;
133}
134
135static void imx_gpio_direction_input(struct imx_gpio_pin *pin)
136{
137  rtems_interrupt_lock_context lock_context;
138  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
139  pin->gpio->regs->gdir &= ~pin->mask;
140  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
141}
142
143static void imx_gpio_direction_output(struct imx_gpio_pin *pin)
144{
145  rtems_interrupt_lock_context lock_context;
146  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
147  pin->gpio->regs->gdir |= pin->mask;
148  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
149}
150
151static void imx_gpio_set_interrupt_any_edge(struct imx_gpio_pin *pin)
152{
153  rtems_interrupt_lock_context lock_context;
154  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
155  pin->gpio->regs->edge_sel |= pin->mask;
156  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
157}
158
159static void imx_gpio_set_interrupt_mode(struct imx_gpio_pin *pin, uint32_t mode)
160{
161  size_t i;
162
163  for (i=0; i < 32; ++i) {
164    if ((pin->mask & (1u << i)) != 0) {
165      volatile uint32_t *icr;
166      size_t shift;
167      rtems_interrupt_lock_context lock_context;
168
169      if (i < 16) {
170        icr = &pin->gpio->regs->icr1;
171        shift = 2 * i;
172      } else {
173        icr = &pin->gpio->regs->icr2;
174        shift = 2 * (i - 16);
175      }
176
177      rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
178      *icr = (*icr & ~(3u << shift)) | (mode << shift);
179      rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
180    }
181  }
182}
183
184rtems_status_code imx_gpio_init_from_fdt_property (
185  struct imx_gpio_pin *pin,
186  int node_offset,
187  const char *property,
188  enum imx_gpio_mode mode,
189  size_t index
190)
191{
192  int len;
193  const uint32_t *val;
194  rtems_status_code sc = RTEMS_SUCCESSFUL;
195  const void *fdt;
196  uint32_t gpio_regs;
197  const unsigned pin_length_dwords = 3;
198  const unsigned pin_length_bytes = (pin_length_dwords * sizeof(uint32_t));
199  uint32_t gpio_phandle;
200  uint32_t pin_nr;
201  int cfgnode;
202
203  memset(pin, 0, sizeof(*pin));
204
205  fdt = bsp_fdt_get();
206  val = fdt_getprop(fdt, node_offset, property, &len);
207  if (val == NULL || (len % pin_length_bytes != 0) ||
208      (index >= len / pin_length_bytes)) {
209    sc = RTEMS_UNSATISFIED;
210  }
211  if (sc == RTEMS_SUCCESSFUL) {
212    pin_nr = fdt32_to_cpu(val[1 + index * pin_length_dwords]);
213    gpio_phandle = fdt32_to_cpu(val[0 + index * pin_length_dwords]);
214
215    cfgnode = fdt_node_offset_by_phandle(fdt, gpio_phandle);
216    val = fdt_getprop(fdt, cfgnode, "reg", &len);
217    if (len > 0) {
218      gpio_regs = fdt32_to_cpu(val[0]);
219    } else {
220      sc = RTEMS_UNSATISFIED;
221    }
222  }
223  if (sc == RTEMS_SUCCESSFUL) {
224    pin->gpio = imx_gpio_get_by_register((void *)gpio_regs);
225    pin->mask = 1u << pin_nr;
226    pin->shift = pin_nr;
227    pin->mode = mode;
228  }
229  if (sc == RTEMS_SUCCESSFUL) {
230    imx_gpio_init(pin);
231  }
232
233  return sc;
234}
235
236rtems_vector_number imx_gpio_get_irq_of_node(
237  const void *fdt,
238  int node,
239  size_t index
240)
241{
242  const uint32_t *val;
243  uint32_t pin;
244  int parent;
245  size_t parent_index;
246  int len;
247
248  val = fdt_getprop(fdt, node, "interrupts", &len);
249  if (val == NULL || len < (int) ((index + 1) * 8)) {
250    return UINT32_MAX;
251  }
252  pin = fdt32_to_cpu(val[index * 2]);
253  if (pin < 16) {
254    parent_index = 0;
255  } else {
256    parent_index = 1;
257  }
258
259  val = fdt_getprop(fdt, node, "interrupt-parent", &len);
260  if (len != 4) {
261    return UINT32_MAX;
262  }
263  parent = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[0]));
264
265  return imx_get_irq_of_node(fdt, parent, parent_index);
266}
267
268void imx_gpio_init (struct imx_gpio_pin *pin)
269{
270  switch (pin->mode) {
271  case (IMX_GPIO_MODE_INTERRUPT_LOW):
272    imx_gpio_direction_input(pin);
273    imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_LOW_LEVEL);
274    break;
275  case (IMX_GPIO_MODE_INTERRUPT_HIGH):
276    imx_gpio_direction_input(pin);
277    imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_HIGH_LEVEL);
278    break;
279  case (IMX_GPIO_MODE_INTERRUPT_RISING):
280    imx_gpio_direction_input(pin);
281    imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_RISING_EDGE);
282    break;
283  case (IMX_GPIO_MODE_INTERRUPT_FALLING):
284    imx_gpio_direction_input(pin);
285    imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE);
286    break;
287  case (IMX_GPIO_MODE_INTERRUPT_ANY_EDGE):
288    imx_gpio_direction_input(pin);
289    imx_gpio_set_interrupt_any_edge(pin);
290    /* Interrupt mode isn't really relevant here. Just set it to get
291     * a defined behaviour in case of a bug. */
292    imx_gpio_set_interrupt_mode(pin, IMX_GPIO_ICR_FALLING_EDGE);
293    break;
294  case (IMX_GPIO_MODE_INPUT):
295    imx_gpio_direction_input(pin);
296    break;
297  case (IMX_GPIO_MODE_OUTPUT):
298    imx_gpio_direction_output(pin);
299    break;
300  default:
301    assert(false);
302    break;
303  }
304}
305
306void imx_gpio_set_output(struct imx_gpio_pin *pin, uint32_t set)
307{
308  rtems_interrupt_lock_context lock_context;
309  set <<= pin->shift;
310  set &= pin->mask;
311  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
312  pin->gpio->regs->dr = (pin->gpio->regs->dr & ~pin->mask) | set;
313  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
314}
315
316void imx_gpio_toggle_output(struct imx_gpio_pin *pin)
317{
318  rtems_interrupt_lock_context lock_context;
319  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
320  pin->gpio->regs->dr = (pin->gpio->regs->dr ^ pin->mask);
321  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
322}
323
324uint32_t imx_gpio_get_input(struct imx_gpio_pin *pin)
325{
326  return (pin->gpio->regs->dr & pin->mask) >> pin->shift;
327}
328
329void imx_gpio_int_disable(struct imx_gpio_pin *pin)
330{
331  rtems_interrupt_lock_context lock_context;
332  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
333  pin->gpio->regs->imr &= ~pin->mask;
334  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
335}
336
337void imx_gpio_int_enable(struct imx_gpio_pin *pin)
338{
339  rtems_interrupt_lock_context lock_context;
340  rtems_interrupt_lock_acquire(&pin->gpio->lock, &lock_context);
341  pin->gpio->regs->imr |= pin->mask;
342  rtems_interrupt_lock_release(&pin->gpio->lock, &lock_context);
343}
344
345uint32_t imx_gpio_get_isr(struct imx_gpio_pin *pin)
346{
347  return (pin->gpio->regs->isr & pin->mask) >> pin->shift;
348}
349
350void imx_gpio_clear_isr(struct imx_gpio_pin *pin, uint32_t clr)
351{
352  pin->gpio->regs->isr = (clr << pin->shift) & pin->mask;
353}
354
355RTEMS_SYSINIT_ITEM(
356  imx_gpio_attach,
357  RTEMS_SYSINIT_DEVICE_DRIVERS,
358  RTEMS_SYSINIT_ORDER_FIRST
359);
Note: See TracBrowser for help on using the repository browser.