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

4.104.115
Last change on this file since e08dbc5 was e08dbc5, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 11/03/09 at 18:45:04

various PowerPC code maintenance

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