source: rtems/c/src/lib/libbsp/arm/lpc24xx/misc/io.c @ 0a66c126

4.115
Last change on this file since 0a66c126 was 0a66c126, checked in by Pavel Pisa <ppisa@…>, on Aug 16, 2014 at 2:15:17 PM

lpc24xx/lpc17xx: lpc24xx_pin_set_function() keep LPC4088 W type pin in digital mode for non-analog function.

The problem wit incorrect switching of pins into analog mode manifestes
on LPC4088 based board.

LPC4088 implements pin P1.17 (ENET_MDIO) as new W type (digital pin
with analog option). The pin was listed as D category on LPC1788
which does not have analog mode control bit. If analog option is
not explicitly switched off on LPC4088 then the pin does not work
as digital pin.

Code tested on LPC1788 and no problems has been observed even that
manual specifies the IOCON_ADMODE field as reserved and should
be written as zero. But even RTEMS lpc24xx_gpio_config sets this
bit unconditionally.

Signed-off-by: Pavel Pisa <ppisa@…>

  • Property mode set to 100644
File size: 15.1 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup lpc24xx_io
5 *
6 * @brief Input and output module.
7 */
8
9/*
10 * Copyright (c) 2009-2012 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Obere Lagerstr. 30
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23#include <bsp.h>
24#include <bsp/io.h>
25#include <bsp/start.h>
26#include <bsp/system-clocks.h>
27
28#define LPC24XX_PIN_SELECT(index) ((index) >> 4U)
29
30#define LPC24XX_PIN_SELECT_SHIFT(index) (((index) & 0xfU) << 1U)
31
32#define LPC24XX_PIN_SELECT_MASK 0x3U
33
34rtems_status_code lpc24xx_gpio_config(
35  unsigned index,
36  lpc24xx_gpio_settings settings
37)
38{
39  if (index <= LPC24XX_IO_INDEX_MAX) {
40    rtems_interrupt_level level;
41    uint32_t port = LPC24XX_IO_PORT(index);
42    uint32_t port_bit = LPC24XX_IO_PORT_BIT(index);
43    uint32_t output = (settings & LPC24XX_GPIO_OUTPUT) != 0 ? 1U : 0U;
44    uint32_t resistor = settings & 0x3U;
45    #ifdef ARM_MULTILIB_ARCH_V4
46      uint32_t select = LPC24XX_PIN_SELECT(index);
47      uint32_t shift = LPC24XX_PIN_SELECT_SHIFT(index);
48
49      /* Get resistor flags */
50      switch (resistor) {
51        case LPC24XX_GPIO_RESISTOR_PULL_UP:
52          resistor = 0x0U;
53          break;
54        case LPC24XX_GPIO_RESISTOR_NONE:
55          resistor = 0x2U;
56          break;
57        case LPC24XX_GPIO_RESISTOR_PULL_DOWN:
58          resistor = 0x3U;
59          break;
60        default:
61          return RTEMS_INVALID_NUMBER;
62      }
63    #else
64      uint32_t iocon_mask = IOCON_HYS | IOCON_INV
65        | IOCON_SLEW | IOCON_OD | IOCON_FILTER;
66      uint32_t iocon = (settings & iocon_mask) | IOCON_ADMODE;
67      uint32_t iocon_invalid = settings & ~(iocon_mask | LPC24XX_GPIO_OUTPUT);
68
69      /* Get resistor flags */
70      switch (resistor) {
71        case LPC24XX_GPIO_RESISTOR_NONE:
72          resistor = IOCON_MODE(0);
73          break;
74        case LPC24XX_GPIO_RESISTOR_PULL_DOWN:
75          resistor = IOCON_MODE(1);
76          break;
77        case LPC24XX_GPIO_RESISTOR_PULL_UP:
78          resistor = IOCON_MODE(2);
79          break;
80        case LPC17XX_GPIO_HYSTERESIS:
81          resistor = IOCON_MODE(3);
82          break;
83      }
84      iocon |= resistor;
85
86      if (iocon_invalid != 0) {
87        return RTEMS_INVALID_NUMBER;
88      }
89
90      if (output && (settings & LPC17XX_GPIO_INPUT_INVERT) != 0) {
91        return RTEMS_INVALID_NUMBER;
92      }
93
94      if ((settings & LPC17XX_GPIO_INPUT_FILTER) == 0) {
95        iocon |= IOCON_FILTER;
96      } else {
97        iocon &= ~IOCON_FILTER;
98      }
99    #endif
100
101    rtems_interrupt_disable(level);
102
103    #ifdef ARM_MULTILIB_ARCH_V4
104      /* Resistor */
105      LPC24XX_PINMODE [select] =
106        (LPC24XX_PINMODE [select] & ~(LPC24XX_PIN_SELECT_MASK << shift))
107          | ((resistor & LPC24XX_PIN_SELECT_MASK) << shift);
108    #else
109      LPC17XX_IOCON [index] = iocon;
110    #endif
111
112    rtems_interrupt_flash(level);
113
114    /* Input or output */
115    LPC24XX_FIO [port].dir =
116      (LPC24XX_FIO [port].dir & ~(1U << port_bit)) | (output << port_bit);
117
118    rtems_interrupt_enable(level);
119  } else {
120    return RTEMS_INVALID_ID;
121  }
122
123  return RTEMS_SUCCESSFUL;
124}
125
126#define LPC24XX_MODULE_ENTRY(mod, pwr, clk, idx) \
127  [mod] = { \
128    .power = pwr, \
129    .clock = clk, \
130    .index = idx \
131  }
132
133typedef struct {
134  unsigned char power : 1;
135  unsigned char clock : 1;
136  unsigned char index : 6;
137} lpc24xx_module_entry;
138
139static const lpc24xx_module_entry lpc24xx_module_table [] = {
140  #ifdef ARM_MULTILIB_ARCH_V4
141    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_ACF, 0, 1, 15),
142  #endif
143  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_ADC, 1, 1, 12),
144  #ifdef ARM_MULTILIB_ARCH_V4
145    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_BAT_RAM, 0, 1, 16),
146  #endif
147  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_CAN_0, 1, 1, 13),
148  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_CAN_1, 1, 1, 14),
149  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_DAC, 0, 1, 11),
150  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_EMC, 1, 0, 11),
151  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_ETHERNET, 1, 0, 30),
152  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_GPDMA, 1, 1, 29),
153  #ifdef ARM_MULTILIB_ARCH_V4
154    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_GPIO, 0, 1, 17),
155  #else
156    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_GPIO, 0, 1, 15),
157  #endif
158  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_I2C_0, 1, 1, 7),
159  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_I2C_1, 1, 1, 19),
160  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_I2C_2, 1, 1, 26),
161  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_I2S, 1, 1, 27),
162  #ifdef ARM_MULTILIB_ARCH_V4
163    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_LCD, 1, 0, 20),
164  #else
165    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_LCD, 1, 0, 0),
166  #endif
167  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_MCI, 1, 1, 28),
168  #ifdef ARM_MULTILIB_ARCH_V7M
169    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_MCPWM, 1, 1, 17),
170  #endif
171  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_PCB, 0, 1, 18),
172  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_PWM_0, 1, 1, 5),
173  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_PWM_1, 1, 1, 6),
174  #ifdef ARM_MULTILIB_ARCH_V7M
175    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_QEI, 1, 1, 18),
176  #endif
177  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_RTC, 1, 1, 9),
178  #ifdef ARM_MULTILIB_ARCH_V4
179    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_SPI, 1, 1, 8),
180  #endif
181  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_SSP_0, 1, 1, 21),
182  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_SSP_1, 1, 1, 10),
183  #ifdef ARM_MULTILIB_ARCH_V7M
184    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_SSP_2, 1, 1, 20),
185  #endif
186  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_SYSCON, 0, 1, 30),
187  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_TIMER_0, 1, 1, 1),
188  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_TIMER_1, 1, 1, 2),
189  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_TIMER_2, 1, 1, 22),
190  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_TIMER_3, 1, 1, 23),
191  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_UART_0, 1, 1, 3),
192  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_UART_1, 1, 1, 4),
193  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_UART_2, 1, 1, 24),
194  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_UART_3, 1, 1, 25),
195  #ifdef ARM_MULTILIB_ARCH_V7M
196    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_UART_4, 1, 1, 8),
197  #endif
198  #ifdef ARM_MULTILIB_ARCH_V4
199    LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_WDT, 0, 1, 0),
200  #endif
201  LPC24XX_MODULE_ENTRY(LPC24XX_MODULE_USB, 1, 0, 31)
202};
203
204static rtems_status_code lpc24xx_module_do_enable(
205  lpc24xx_module module,
206  lpc24xx_module_clock clock,
207  bool enable
208)
209{
210  rtems_interrupt_level level;
211  bool has_power = false;
212  bool has_clock = false;
213  unsigned index = 0;
214  #ifdef ARM_MULTILIB_ARCH_V7M
215    volatile lpc17xx_scb *scb = &LPC17XX_SCB;
216  #endif
217
218  if ((unsigned) module >= LPC24XX_MODULE_COUNT) {
219      return RTEMS_INVALID_ID;
220  }
221
222  #ifdef ARM_MULTILIB_ARCH_V4
223    if (clock == LPC24XX_MODULE_PCLK_DEFAULT) {
224      #if LPC24XX_PCLKDIV == 1U
225        clock = LPC24XX_MODULE_CCLK;
226      #elif LPC24XX_PCLKDIV == 2U
227        clock = LPC24XX_MODULE_CCLK_2;
228      #elif LPC24XX_PCLKDIV == 4U
229        clock = LPC24XX_MODULE_CCLK_4;
230      #elif LPC24XX_PCLKDIV == 8U
231        clock = LPC24XX_MODULE_CCLK_8;
232      #endif
233    }
234
235    if ((clock & ~LPC24XX_MODULE_CLOCK_MASK) != 0U) {
236      return RTEMS_INVALID_CLOCK;
237    }
238  #else
239    if (clock != LPC24XX_MODULE_PCLK_DEFAULT) {
240      return RTEMS_INVALID_CLOCK;
241    }
242  #endif
243
244  has_power = lpc24xx_module_table [module].power;
245  has_clock = lpc24xx_module_table [module].clock;
246  index = lpc24xx_module_table [module].index;
247
248  /* Enable or disable module */
249  if (enable) {
250    if (has_power) {
251      rtems_interrupt_disable(level);
252      #ifdef ARM_MULTILIB_ARCH_V4
253        PCONP |= 1U << index;
254      #else
255        scb->pconp |= 1U << index;
256      #endif
257      rtems_interrupt_enable(level);
258    }
259
260    if (module != LPC24XX_MODULE_USB) {
261      if (has_clock) {
262        #ifdef ARM_MULTILIB_ARCH_V4
263          unsigned clock_shift = 2U * index;
264
265          rtems_interrupt_disable(level);
266          if (clock_shift < 32U) {
267            PCLKSEL0 = (PCLKSEL0 & ~(LPC24XX_MODULE_CLOCK_MASK << clock_shift))
268                | (clock << clock_shift);
269          } else {
270            clock_shift -= 32U;
271            PCLKSEL1 = (PCLKSEL1 & ~(LPC24XX_MODULE_CLOCK_MASK << clock_shift))
272                | (clock << clock_shift);
273          }
274          rtems_interrupt_enable(level);
275        #endif
276      }
277    } else {
278      #ifdef ARM_MULTILIB_ARCH_V4
279        unsigned pllclk = lpc24xx_pllclk();
280        unsigned usbsel = pllclk / 48000000U - 1U;
281
282        if (
283          usbsel > 15U
284            || (usbsel % 2U != 1U)
285            || (pllclk % 48000000U) != 0U
286        ) {
287          return RTEMS_INCORRECT_STATE;
288        }
289
290        USBCLKCFG = usbsel;
291      #else
292        uint32_t pllclk = lpc24xx_pllclk();
293        uint32_t usbclk = 48000000U;
294
295        if (pllclk % usbclk == 0U) {
296          uint32_t usbdiv = pllclk / usbclk;
297
298          scb->usbclksel = LPC17XX_SCB_USBCLKSEL_USBDIV(usbdiv)
299            | LPC17XX_SCB_USBCLKSEL_USBSEL(1);
300        } else {
301          return RTEMS_INCORRECT_STATE;
302        }
303      #endif
304    }
305  } else {
306    if (has_power) {
307      rtems_interrupt_disable(level);
308      #ifdef ARM_MULTILIB_ARCH_V4
309        PCONP &= ~(1U << index);
310      #else
311        scb->pconp &= ~(1U << index);
312      #endif
313      rtems_interrupt_enable(level);
314    }
315  }
316
317  return RTEMS_SUCCESSFUL;
318}
319
320rtems_status_code lpc24xx_module_enable(
321  lpc24xx_module module,
322  lpc24xx_module_clock clock
323)
324{
325  return lpc24xx_module_do_enable(module, clock, true);
326}
327
328rtems_status_code lpc24xx_module_disable(
329  lpc24xx_module module
330)
331{
332  return lpc24xx_module_do_enable(
333    module,
334    LPC24XX_MODULE_PCLK_DEFAULT,
335    false
336  );
337}
338
339bool lpc24xx_module_is_enabled(lpc24xx_module module)
340{
341  bool enabled = false;
342
343  if ((unsigned) module < LPC24XX_MODULE_COUNT) {
344    bool has_power = lpc24xx_module_table [module].power;
345
346    if (has_power) {
347      unsigned index = lpc24xx_module_table [module].index;
348      #ifdef ARM_MULTILIB_ARCH_V4
349        uint32_t pconp = PCONP;
350      #else
351        uint32_t pconp = LPC17XX_SCB.pconp;
352      #endif
353
354      enabled = (pconp & (1U << index)) != 0;
355    } else {
356      enabled = true;
357    }
358  }
359
360  return enabled;
361}
362
363typedef rtems_status_code (*lpc24xx_pin_visitor)(
364  #ifdef ARM_MULTILIB_ARCH_V4
365    volatile uint32_t *pinsel,
366    uint32_t pinsel_mask,
367    uint32_t pinsel_value,
368  #else
369    volatile uint32_t *iocon,
370    lpc24xx_pin_range pin_range,
371  #endif
372  volatile uint32_t *fio_dir,
373  uint32_t fio_bit
374);
375
376static BSP_START_TEXT_SECTION __attribute__((flatten)) rtems_status_code
377lpc24xx_pin_set_function(
378  #ifdef ARM_MULTILIB_ARCH_V4
379    volatile uint32_t *pinsel,
380    uint32_t pinsel_mask,
381    uint32_t pinsel_value,
382  #else
383    volatile uint32_t *iocon,
384    lpc24xx_pin_range pin_range,
385  #endif
386  volatile uint32_t *fio_dir,
387  uint32_t fio_bit
388)
389{
390  #ifdef ARM_MULTILIB_ARCH_V4
391    rtems_interrupt_level level;
392
393    rtems_interrupt_disable(level);
394    *pinsel = (*pinsel & ~pinsel_mask) | pinsel_value;
395    rtems_interrupt_enable(level);
396  #else
397    uint32_t iocon_extra = 0;
398    uint32_t iocon_not_analog = IOCON_ADMODE;
399
400    /* TODO */
401    switch (pin_range.fields.type) {
402      case LPC17XX_PIN_TYPE_ADC:
403      case LPC17XX_PIN_TYPE_DAC:
404        iocon_not_analog = 0;
405        break;
406      case LPC17XX_PIN_TYPE_I2C_FAST_PLUS:
407        iocon_extra |= IOCON_HS;
408        break;
409      case LPC17XX_PIN_TYPE_OPEN_DRAIN:
410        iocon_extra |= IOCON_OD;
411        break;
412      default:
413        break;
414    }
415
416    *iocon = IOCON_FUNC(pin_range.fields.function) | iocon_extra | iocon_not_analog;
417  #endif
418
419  return RTEMS_SUCCESSFUL;
420}
421
422static BSP_START_TEXT_SECTION rtems_status_code lpc24xx_pin_check_function(
423  #ifdef ARM_MULTILIB_ARCH_V4
424    volatile uint32_t *pinsel,
425    uint32_t pinsel_mask,
426    uint32_t pinsel_value,
427  #else
428    volatile uint32_t *iocon,
429    lpc24xx_pin_range pin_range,
430  #endif
431  volatile uint32_t *fio_dir,
432  uint32_t fio_bit
433)
434{
435  #ifdef ARM_MULTILIB_ARCH_V4
436    if ((*pinsel & pinsel_mask) == pinsel_value) {
437      return RTEMS_SUCCESSFUL;
438    } else {
439      return RTEMS_IO_ERROR;
440    }
441  #else
442    /* TODO */
443    return RTEMS_IO_ERROR;
444  #endif
445}
446
447static BSP_START_TEXT_SECTION __attribute__((flatten)) rtems_status_code
448lpc24xx_pin_set_input(
449  #ifdef ARM_MULTILIB_ARCH_V4
450    volatile uint32_t *pinsel,
451    uint32_t pinsel_mask,
452    uint32_t pinsel_value,
453  #else
454    volatile uint32_t *iocon,
455    lpc24xx_pin_range pin_range,
456  #endif
457  volatile uint32_t *fio_dir,
458  uint32_t fio_bit
459)
460{
461  rtems_interrupt_level level;
462
463  rtems_interrupt_disable(level);
464  *fio_dir &= ~fio_bit;
465  #ifdef ARM_MULTILIB_ARCH_V4
466    *pinsel &= ~pinsel_mask;
467  #else
468    *iocon = IOCON_MODE(2) | IOCON_ADMODE | IOCON_FILTER;
469  #endif
470  rtems_interrupt_enable(level);
471
472  return RTEMS_SUCCESSFUL;
473}
474
475static BSP_START_TEXT_SECTION rtems_status_code lpc24xx_pin_check_input(
476  #ifdef ARM_MULTILIB_ARCH_V4
477    volatile uint32_t *pinsel,
478    uint32_t pinsel_mask,
479    uint32_t pinsel_value,
480  #else
481    volatile uint32_t *iocon,
482    lpc24xx_pin_range pin_range,
483  #endif
484  volatile uint32_t *fio_dir,
485  uint32_t fio_bit
486)
487{
488  rtems_status_code sc = RTEMS_IO_ERROR;
489  bool is_input = (*fio_dir & fio_bit) == 0;
490
491  if (is_input) {
492    #ifdef ARM_MULTILIB_ARCH_V4
493      bool is_gpio = (*pinsel & pinsel_mask) == 0;
494    #else
495      bool is_gpio = IOCON_FUNC_GET(*iocon) == 0;
496    #endif
497
498    if (is_gpio) {
499      sc = RTEMS_SUCCESSFUL;
500    }
501  }
502
503  return sc;
504}
505
506static BSP_START_DATA_SECTION const lpc24xx_pin_visitor
507  lpc24xx_pin_visitors [] = {
508  [LPC24XX_PIN_SET_FUNCTION] = lpc24xx_pin_set_function,
509  [LPC24XX_PIN_CHECK_FUNCTION] = lpc24xx_pin_check_function,
510  [LPC24XX_PIN_SET_INPUT] = lpc24xx_pin_set_input,
511  [LPC24XX_PIN_CHECK_INPUT] = lpc24xx_pin_check_input
512};
513
514BSP_START_TEXT_SECTION rtems_status_code lpc24xx_pin_config(
515  const lpc24xx_pin_range *pins,
516  lpc24xx_pin_action action
517)
518{
519  rtems_status_code sc = RTEMS_SUCCESSFUL;
520
521  if ((unsigned) action <= LPC24XX_PIN_CHECK_INPUT) {
522    lpc24xx_pin_visitor visitor = lpc24xx_pin_visitors [action];
523    lpc24xx_pin_range terminal = LPC24XX_PIN_TERMINAL;
524    lpc24xx_pin_range pin_range = *pins;
525    uint32_t previous_port_bit = pin_range.fields.port_bit;
526
527    while (sc == RTEMS_SUCCESSFUL && pin_range.value != terminal.value) {
528      uint32_t port = pin_range.fields.port;
529      uint32_t port_bit = pin_range.fields.port_bit;
530      uint32_t port_bit_last = port_bit;
531      uint32_t range = pin_range.fields.range;
532      #ifdef ARM_MULTILIB_ARCH_V4
533        uint32_t function = pin_range.fields.function;
534      #endif
535      volatile uint32_t *fio_dir = &LPC24XX_FIO [port].dir;
536
537      if (range) {
538        port_bit = previous_port_bit;
539      }
540
541      while (sc == RTEMS_SUCCESSFUL && port_bit <= port_bit_last) {
542        uint32_t index = LPC24XX_IO_INDEX_BY_PORT(port, port_bit);
543        uint32_t fio_bit = 1U << port_bit;
544        #ifdef ARM_MULTILIB_ARCH_V4
545          uint32_t select = LPC24XX_PIN_SELECT(index);
546          uint32_t shift = LPC24XX_PIN_SELECT_SHIFT(index);
547          volatile uint32_t *pinsel = &LPC24XX_PINSEL [select];
548          uint32_t pinsel_mask = LPC24XX_PIN_SELECT_MASK << shift;
549          uint32_t pinsel_value = (function & LPC24XX_PIN_SELECT_MASK) << shift;
550
551          sc = (*visitor)(pinsel, pinsel_mask, pinsel_value, fio_dir, fio_bit);
552        #else
553          volatile uint32_t *iocon = &LPC17XX_IOCON [index];
554
555          sc = (*visitor)(iocon, pin_range, fio_dir, fio_bit);
556        #endif
557
558        ++port_bit;
559      }
560
561      ++pins;
562      previous_port_bit = port_bit;
563      pin_range = *pins;
564    }
565  } else {
566    sc = RTEMS_NOT_DEFINED;
567  }
568
569  return sc;
570}
Note: See TracBrowser for help on using the repository browser.