source: rtems/c/src/lib/libcpu/powerpc/shared/include/powerpc-utility.h @ 3fcc78ae

4.104.115
Last change on this file since 3fcc78ae was 3fcc78ae, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 10/30/09 at 19:28:46

move timebase access functions from cpukit to libcpu

  • Property mode set to 100644
File size: 13.6 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup powerpc_shared
5 *
6 * @brief General purpose assembler macros, linker command file support and
7 * some inline functions for direct register access.
8 */
9
10/*
11 * Copyright (c) 2008
12 * Embedded Brains GmbH
13 * Obere Lagerstr. 30
14 * D-82178 Puchheim
15 * Germany
16 * rtems@embedded-brains.de
17 *
18 * access function for Device Control Registers inspired by "ppc405common.h"
19 * from Michael Hamel ADInstruments May 2008
20 *
21 * The license and distribution terms for this file may be found in the file
22 * LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
23 */
24
25/**
26 * @defgroup powerpc_shared Shared PowerPC Code
27 */
28
29#ifndef LIBCPU_POWERPC_UTILITY_H
30#define LIBCPU_POWERPC_UTILITY_H
31
32#ifndef ASM
33  #include <rtems.h>
34#endif
35
36#include <rtems/score/cpu.h>
37#include <rtems/powerpc/registers.h>
38#include <rtems/powerpc/powerpc.h>
39
40#ifndef ASM
41
42#include <rtems/bspIo.h>
43#include <rtems/system.h>
44
45#include <libcpu/cpuIdent.h>
46
47#define LINKER_SYMBOL(sym) extern char sym []
48
49/**
50 * @brief Read one byte from @a src.
51 */
52static inline uint8_t ppc_read_byte(const volatile void *src)
53{
54  uint8_t value;
55
56  asm volatile (
57    "lbz %0, 0(%1)"
58    : "=r" (value)
59    : "b" (src)
60  );
61
62  return value;
63}
64
65/**
66 * @brief Read one half word from @a src.
67 */
68static inline uint16_t ppc_read_half_word(const volatile void *src)
69{
70  uint16_t value;
71
72  asm volatile (
73    "lhz %0, 0(%1)"
74    : "=r" (value)
75    : "b" (src)
76  );
77
78  return value;
79}
80
81/**
82 * @brief Read one word from @a src.
83 */
84static inline uint32_t ppc_read_word(const volatile void *src)
85{
86  uint32_t value;
87
88  asm volatile (
89    "lwz %0, 0(%1)"
90    : "=r" (value)
91    : "b" (src)
92  );
93
94  return value;
95}
96
97/**
98 * @brief Write one byte @a value to @a dest.
99 */
100static inline void ppc_write_byte(uint8_t value, volatile void *dest)
101{
102  asm volatile (
103    "stb %0, 0(%1)"
104    :
105    : "r" (value), "b" (dest)
106  );
107}
108
109/**
110 * @brief Write one half word @a value to @a dest.
111 */
112static inline void ppc_write_half_word(uint16_t value, volatile void *dest)
113{
114  asm volatile (
115    "sth %0, 0(%1)"
116    :
117    : "r" (value), "b" (dest)
118  );
119}
120
121/**
122 * @brief Write one word @a value to @a dest.
123 */
124static inline void ppc_write_word(uint32_t value, volatile void *dest)
125{
126  asm volatile (
127    "stw %0, 0(%1)" :
128    : "r" (value), "b" (dest)
129  );
130}
131
132
133static inline void *ppc_stack_pointer(void)
134{
135  void *sp;
136
137  asm volatile (
138    "mr %0, 1"
139    : "=r" (sp)
140  );
141
142  return sp;
143}
144
145static inline void ppc_set_stack_pointer(void *sp)
146{
147  asm volatile (
148    "mr 1, %0"
149    :
150    : "r" (sp)
151  );
152}
153
154static inline void *ppc_link_register(void)
155{
156  void *lr;
157
158  asm volatile (
159    "mflr %0"
160    : "=r" (lr)
161  );
162
163  return lr;
164}
165
166static inline void ppc_set_link_register(void *lr)
167{
168  asm volatile (
169    "mtlr %0"
170    :
171    : "r" (lr)
172  );
173}
174
175static inline uint32_t ppc_machine_state_register(void)
176{
177  uint32_t msr;
178
179  asm volatile (
180    "mfmsr %0"
181    : "=r" (msr)
182  );
183
184  return msr;
185}
186
187static inline void ppc_set_machine_state_register(uint32_t msr)
188{
189  asm volatile (
190    "mtmsr %0"
191    :
192    : "r" (msr)
193  );
194}
195
196static inline void ppc_synchronize_data(void)
197{
198  RTEMS_COMPILER_MEMORY_BARRIER();
199
200  asm volatile ("sync");
201}
202
203static inline void ppc_synchronize_instructions(void)
204{
205  RTEMS_COMPILER_MEMORY_BARRIER();
206
207  asm volatile ("isync");
208}
209
210/**
211 * @brief Enables external exceptions.
212 *
213 * You can use this function to enable the external exceptions and restore the
214 * machine state with ppc_external_exceptions_disable() later.
215 */
216static inline uint32_t ppc_external_exceptions_enable(void)
217{
218  uint32_t current_msr;
219  uint32_t new_msr;
220
221  RTEMS_COMPILER_MEMORY_BARRIER();
222
223  asm volatile (
224    "mfmsr %0;"
225    "ori %1, %0, 0x8000;"
226    "mtmsr %1"
227    : "=r" (current_msr), "=r" (new_msr)
228  );
229
230  return current_msr;
231}
232
233/**
234 * @brief Restores machine state.
235 *
236 * @see ppc_external_exceptions_enable()
237 */
238static inline void ppc_external_exceptions_disable(uint32_t msr)
239{
240  ppc_set_machine_state_register(msr);
241
242  RTEMS_COMPILER_MEMORY_BARRIER();
243}
244
245#ifndef ASM
246/*
247 *  Simple spin delay in microsecond units for device drivers.
248 *  This is very dependent on the clock speed of the target.
249 */
250
251#if defined(mpx8xx) || defined(mpc860) || defined(mpc821)
252/* Wonderful bookE doesn't have mftb/mftbu; they only
253 * define the TBRU/TBRL SPRs so we use these. Luckily,
254 * we run in supervisory mode so that should work on
255 * all CPUs. In user mode we'd have a problem...
256 * 2007/11/30, T.S.
257 *
258 * OTOH, PSIM currently lacks support for reading
259 * SPRs 268/269. You need GDB patch sim/2376 to avoid
260 * a crash...
261 * OTOH, the MPC8xx do not allow to read the timebase registers via mfspr.
262 * we NEED a mftb to access the time base.
263 * 2009/10/30 Th. D.
264 */
265#define CPU_Get_timebase_low( _value ) \
266    asm volatile( "mftb  %0" : "=r" (_value) )
267#else
268#define CPU_Get_timebase_low( _value ) \
269    asm volatile( "mfspr %0,268" : "=r" (_value) )
270#endif
271
272/* Must be provided for rtems_bsp_delay to work */
273extern     uint32_t bsp_clicks_per_usec;
274
275#define rtems_bsp_delay( _microseconds ) \
276  do { \
277    uint32_t   start, ticks, now; \
278    CPU_Get_timebase_low( start ) ; \
279    ticks = (_microseconds) * bsp_clicks_per_usec; \
280    do \
281      CPU_Get_timebase_low( now ) ; \
282    while (now - start < ticks); \
283  } while (0)
284
285#define rtems_bsp_delay_in_bus_cycles( _cycles ) \
286  do { \
287    uint32_t   start, now; \
288    CPU_Get_timebase_low( start ); \
289    do \
290      CPU_Get_timebase_low( now ); \
291    while (now - start < (_cycles)); \
292  } while (0)
293
294#endif /* ASM */
295
296#ifndef ASM
297/*
298 *  Routines to access the decrementer register
299 */
300
301#define PPC_Set_decrementer( _clicks ) \
302  do { \
303    asm volatile( "mtdec %0" : : "r" ((_clicks)) ); \
304  } while (0)
305
306#define PPC_Get_decrementer( _clicks ) \
307    asm volatile( "mfdec  %0" : "=r" (_clicks) )
308
309#endif /* ASM */
310
311#ifndef ASM
312/*
313 *  Routines to access the time base register
314 */
315
316static inline uint64_t PPC_Get_timebase_register( void )
317{
318  uint32_t tbr_low;
319  uint32_t tbr_high;
320  uint32_t tbr_high_old;
321  uint64_t tbr;
322
323  do {
324#if defined(mpx8xx) || defined(mpc860) || defined(mpc821)
325/* See comment above (CPU_Get_timebase_low) */
326    asm volatile( "mftbu %0" : "=r" (tbr_high_old));
327    asm volatile( "mftb  %0" : "=r" (tbr_low));
328    asm volatile( "mftbu %0" : "=r" (tbr_high));
329#else
330    asm volatile( "mfspr %0, 269" : "=r" (tbr_high_old));
331    asm volatile( "mfspr %0, 268" : "=r" (tbr_low));
332    asm volatile( "mfspr %0, 269" : "=r" (tbr_high));
333#endif
334  } while ( tbr_high_old != tbr_high );
335
336  tbr = tbr_high;
337  tbr <<= 32;
338  tbr |= tbr_low;
339  return tbr;
340}
341
342static inline  void PPC_Set_timebase_register (uint64_t tbr)
343{
344  uint32_t tbr_low;
345  uint32_t tbr_high;
346
347  tbr_low = (uint32_t) tbr;
348  tbr_high = (uint32_t) (tbr >> 32);
349  asm volatile( "mtspr 284, %0" : : "r" (tbr_low));
350  asm volatile( "mtspr 285, %0" : : "r" (tbr_high));
351 
352}
353#endif /* ASM */
354
355static inline uint32_t ppc_decrementer_register(void)
356{
357  uint32_t dec;
358
359  PPC_Get_decrementer(dec);
360
361  return dec;
362}
363
364static inline void ppc_set_decrementer_register(uint32_t dec)
365{
366  PPC_Set_decrementer(dec);
367}
368
369/**
370 * @brief Preprocessor magic for stringification of @a x.
371 */
372#define PPC_STRINGOF(x) #x
373
374/**
375 * @brief Returns the value of the Special Purpose Register with number @a spr.
376 *
377 * @note This macro uses a GNU C extension.
378 */
379#define PPC_SPECIAL_PURPOSE_REGISTER(spr) \
380  ({ \
381    uint32_t val; \
382    asm volatile (\
383      "mfspr %0, " PPC_STRINGOF(spr) \
384      : "=r" (val) \
385    ); \
386    val;\
387  } )
388
389/**
390 * @brief Sets the Special Purpose Register with number @a spr to the value in
391 * @a val.
392 */
393#define PPC_SET_SPECIAL_PURPOSE_REGISTER(spr, val) \
394  do { \
395    asm volatile (\
396      "mtspr " PPC_STRINGOF(spr) ", %0" \
397      : \
398      : "r" (val) \
399    ); \
400  } while (0)
401
402/**
403 * @brief Sets in the Special Purpose Register with number @a spr all bits
404 * which are set in @a bits.
405 *
406 * Interrupts are disabled throughout this operation.
407 */
408#define PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS(spr, bits) \
409  do { \
410    rtems_interrupt_level level; \
411    uint32_t val; \
412    uint32_t mybits = bits; \
413    rtems_interrupt_disable(level); \
414    val = PPC_SPECIAL_PURPOSE_REGISTER(spr); \
415    val |= mybits; \
416    PPC_SET_SPECIAL_PURPOSE_REGISTER(spr, val); \
417    rtems_interrupt_enable(level); \
418  } while (0)
419
420/**
421 * @brief Sets in the Special Purpose Register with number @a spr all bits
422 * which are set in @a bits.  The previous register value will be masked with
423 * @a mask.
424 *
425 * Interrupts are disabled throughout this operation.
426 */
427#define PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS_MASKED(spr, bits, mask) \
428  do { \
429    rtems_interrupt_level level; \
430    uint32_t val; \
431    uint32_t mybits = bits; \
432    uint32_t mymask = mask; \
433    rtems_interrupt_disable(level); \
434    val = PPC_SPECIAL_PURPOSE_REGISTER(spr); \
435    val &= ~mymask; \
436    val |= mybits; \
437    PPC_SET_SPECIAL_PURPOSE_REGISTER(spr, val); \
438    rtems_interrupt_enable(level); \
439  } while (0)
440
441/**
442 * @brief Clears in the Special Purpose Register with number @a spr all bits
443 * which are set in @a bits.
444 *
445 * Interrupts are disabled throughout this operation.
446 */
447#define PPC_CLEAR_SPECIAL_PURPOSE_REGISTER_BITS(spr, bits) \
448  do { \
449    rtems_interrupt_level level; \
450    uint32_t val; \
451    uint32_t mybits = bits; \
452    rtems_interrupt_disable(level); \
453    val = PPC_SPECIAL_PURPOSE_REGISTER(spr); \
454    val &= ~mybits; \
455    PPC_SET_SPECIAL_PURPOSE_REGISTER(spr, val); \
456    rtems_interrupt_enable(level); \
457  } while (0)
458
459/**
460 * @brief Returns the value of the Device Control Register with number @a dcr.
461 *
462 * The PowerPC 4XX family has Device Control Registers.
463 *
464 * @note This macro uses a GNU C extension.
465 */
466#define PPC_DEVICE_CONTROL_REGISTER(dcr) \
467  ({ \
468    uint32_t val; \
469    asm volatile (\
470      "mfdcr %0, " PPC_STRINGOF(dcr) \
471      : "=r" (val) \
472    ); \
473    val;\
474  } )
475
476/**
477 * @brief Sets the Device Control Register with number @a dcr to the value in
478 * @a val.
479 *
480 * The PowerPC 4XX family has Device Control Registers.
481 */
482#define PPC_SET_DEVICE_CONTROL_REGISTER(dcr, val) \
483  do { \
484    asm volatile (\
485      "mtdcr " PPC_STRINGOF(dcr) ", %0" \
486      : \
487      : "r" (val) \
488    ); \
489  } while (0)
490
491/**
492 * @brief Sets in the Device Control Register with number @a dcr all bits
493 * which are set in @a bits.
494 *
495 * Interrupts are disabled throughout this operation.
496 */
497#define PPC_SET_DEVICE_CONTROL_REGISTER_BITS(dcr, bits) \
498  do { \
499    rtems_interrupt_level level; \
500    uint32_t val; \
501    uint32_t mybits = bits; \
502    rtems_interrupt_disable(level); \
503    val = PPC_DEVICE_CONTROL_REGISTER(dcr); \
504    val |= mybits; \
505    PPC_SET_DEVICE_CONTROL_REGISTER(dcr, val); \
506    rtems_interrupt_enable(level); \
507  } while (0)
508
509/**
510 * @brief Sets in the Device Control Register with number @a dcr all bits
511 * which are set in @a bits.  The previous register value will be masked with
512 * @a mask.
513 *
514 * Interrupts are disabled throughout this operation.
515 */
516#define PPC_SET_DEVICE_CONTROL_REGISTER_BITS_MASKED(dcr, bits, mask) \
517  do { \
518    rtems_interrupt_level level; \
519    uint32_t val; \
520    uint32_t mybits = bits; \
521    uint32_t mymask = mask; \
522    rtems_interrupt_disable(level); \
523    val = PPC_DEVICE_CONTROL_REGISTER(dcr); \
524    val &= ~mymask; \
525    val |= mybits; \
526    PPC_SET_DEVICE_CONTROL_REGISTER(dcr, val); \
527    rtems_interrupt_enable(level); \
528  } while (0)
529
530/**
531 * @brief Clears in the Device Control Register with number @a dcr all bits
532 * which are set in @a bits.
533 *
534 * Interrupts are disabled throughout this operation.
535 */
536#define PPC_CLEAR_DEVICE_CONTROL_REGISTER_BITS(dcr, bits) \
537  do { \
538    rtems_interrupt_level level; \
539    uint32_t val; \
540    uint32_t mybits = bits; \
541    rtems_interrupt_disable(level); \
542    val = PPC_DEVICE_CONTROL_REGISTER(dcr); \
543    val &= ~mybits; \
544    PPC_SET_DEVICE_CONTROL_REGISTER(dcr, val); \
545    rtems_interrupt_enable(level); \
546  } while (0)
547
548static inline uint32_t ppc_time_base(void)
549{
550  uint32_t val;
551
552  CPU_Get_timebase_low(val);
553
554  return val;
555}
556
557static inline void ppc_set_time_base(uint32_t val)
558{
559  PPC_SET_SPECIAL_PURPOSE_REGISTER(TBWL, val);
560}
561
562static inline uint32_t ppc_time_base_upper(void)
563{
564  return PPC_SPECIAL_PURPOSE_REGISTER(TBRU);
565}
566
567static inline void ppc_set_time_base_upper(uint32_t val)
568{
569  PPC_SET_SPECIAL_PURPOSE_REGISTER(TBWU, val);
570}
571
572static inline uint64_t ppc_time_base_64(void)
573{
574  return PPC_Get_timebase_register();
575}
576
577static inline void ppc_set_time_base_64(uint64_t val)
578{
579  PPC_Set_timebase_register(val);
580}
581
582void ppc_code_copy(void *dest, const void *src, size_t n);
583
584#else /* ASM */
585
586#include <rtems/asm.h>
587
588.macro LA reg, addr
589        lis     \reg, (\addr)@h
590        ori     \reg, \reg, (\addr)@l
591.endm
592
593.macro LWI reg, value
594        lis \reg, (\value)@h
595        ori     \reg, \reg, (\value)@l
596.endm
597
598.macro LW reg, addr
599        lis     \reg, \addr@ha
600        lwz     \reg, \addr@l(\reg)
601.endm
602
603/*
604 * Tests the bits in reg1 against the bits set in mask.  A match is indicated
605 * by EQ = 0 in CR0.  A mismatch is indicated by EQ = 1 in CR0.  The register
606 * reg2 is used to load the mask.
607 */
608.macro  TSTBITS reg1, reg2, mask
609        LWI     \reg2, \mask
610        and     \reg1, \reg1, \reg2
611        cmplw   \reg1, \reg2
612.endm   
613       
614.macro  SETBITS reg1, reg2, mask
615        LWI     \reg2, \mask
616        or      \reg1, \reg1, \reg2
617.endm
618
619.macro  CLRBITS reg1, reg2, mask
620        LWI     \reg2, \mask
621        andc    \reg1, \reg1, \reg2
622.endm
623
624.macro GLOBAL_FUNCTION name
625        .global \name
626        .type \name, @function
627\name:
628.endm
629
630/*
631 * Obtain interrupt mask
632 */
633.macro GET_INTERRUPT_MASK mask
634        mfspr   \mask, sprg0
635.endm
636
637/*
638 * Disables all asynchronous exeptions (interrupts) which may cause a context
639 * switch.
640 */
641.macro INTERRUPT_DISABLE level, mask
642        mfmsr   \level
643        GET_INTERRUPT_MASK mask=\mask
644        andc    \mask, \level, \mask
645        mtmsr   \mask
646.endm
647
648/*
649 * Restore previous machine state.
650 */
651.macro INTERRUPT_ENABLE level
652        mtmsr   \level
653.endm
654
655#define LINKER_SYMBOL(sym) .extern sym
656
657#endif /* ASM */
658
659#endif /* LIBCPU_POWERPC_UTILITY_H */
Note: See TracBrowser for help on using the repository browser.