Contiki 3.x
llwu.c
Go to the documentation of this file.
1 /**
2  * \file
3  * \author
4  */
5 
6 #include "K60.h"
7 #include "power-modes.h"
8 #include "llwu.h"
9 #include "lib/list.h"
10 
11 #include <stddef.h>
12 
13 #define DEBUG 0
14 #if DEBUG
15 #include "stdio.h"
16 #define PRINTF(...) printf(__VA_ARGS__)
17 #else
18 #define PRINTF(...)
19 #endif
20 
21 LIST(llwu);
22 static char allow_deep_sleep = 1;
23 
24 static void update_llwu();
25 
26 volatile uint32_t llwu_inhibit_lls_sema = 0;
27 volatile uint32_t llwu_inhibit_vlps_sema = 0;
28 volatile uint32_t llwu_inhibit_stop_sema = 0;
29 
30 /* TODO(henrik) Add callbacks before entering deep sleep. */
31 /*---------------------------------------------------------------------------*/
32 void
33 llwu_init()
34 {
35  list_init(llwu);
36  /* Setup Low Leakage Wake-up Unit (LLWU) */
37  BITBAND_REG(SIM->SCGC4, SIM_SCGC4_LLWU_SHIFT) = 1; /* Enable LLWU clock gate */
38 
40 
41  update_llwu();
42 
43  NVIC_EnableIRQ(LLW_IRQn); /* Enable LLWU interrupt */
44 }
45 /*---------------------------------------------------------------------------*/
46 void
47 llwu_register(llwu_control_t *c)
48 {
49  list_add(llwu, c);
50  PRINTF("LLWU: new controller\n");
51 }
52 /*---------------------------------------------------------------------------*/
53 void
54 llwu_set_allow(llwu_control_t *c, char allow)
55 {
56  c->allow_llwu = allow;
57  update_llwu();
58  PRINTF("LLWU: allow LLWU %d\n", allow_deep_sleep);
59 }
60 /*---------------------------------------------------------------------------*/
61 static void
62 update_llwu()
63 {
64  llwu_control_t *n;
65  allow_deep_sleep = 1;
66  for(n = list_head(llwu); n != NULL;) {
67  if(!n->allow_llwu) {
68  allow_deep_sleep = 0;
69  return;
70  }
71  n = list_item_next(n);
72  }
73 }
74 /*---------------------------------------------------------------------------*/
75 void
77 {
78  PRINTF("LLWU: sleep %u....\n", allow_deep_sleep);
79  /* Check if any UARTs are currently receiving data, if we go to STOP mode we
80  * will lose the byte in progress. */
81  /* It is necessary to check the SIM_SCGCx registers to avoid hardfaulting when
82  * we try to read a disabled peripheral */
83  if(!allow_deep_sleep ||
84  ((SIM->SCGC4 & SIM_SCGC4_UART0_MASK) && (UART0->S2 & UART_S2_RAF_MASK)) ||
85  ((SIM->SCGC4 & SIM_SCGC4_UART1_MASK) && (UART1->S2 & UART_S2_RAF_MASK)) ||
86  ((SIM->SCGC4 & SIM_SCGC4_UART2_MASK) && (UART2->S2 & UART_S2_RAF_MASK)) ||
87  ((SIM->SCGC4 & SIM_SCGC4_UART3_MASK) && (UART3->S2 & UART_S2_RAF_MASK)) ||
88  ((SIM->SCGC1 & SIM_SCGC1_UART4_MASK) && (UART4->S2 & UART_S2_RAF_MASK)) ||
89  ((SIM->SCGC1 & SIM_SCGC1_UART5_MASK) && (UART5->S2 & UART_S2_RAF_MASK))) {
90  power_mode_wait();
91  }
92  /* FIXME: Do we need to disable interrupts here? */
93  else if(llwu_inhibit_stop_sema != 0) {
94  /* STOP inhibited, use WAIT instead */
95  PRINTF("LLWU: STOP inhibited\n");
96  power_mode_wait();
97  } else if(llwu_inhibit_vlps_sema != 0) {
98  /* VLPS inhibited, use STOP */
99  power_mode_stop();
100  } else if(llwu_inhibit_lls_sema != 0) {
101  /* LLS inhibited, use VLPS */
102  power_mode_vlps();
103  } else {
104  /* free to stop everything */
105  power_mode_lls();
106  }
107 }
108 /*---------------------------------------------------------------------------*/
109 void
110 llwu_enable_wakeup_module(const llwu_wakeup_module_t module)
111 {
112  if(module < LLWU_WAKEUP_MODULE_END) {
113  LLWU->ME |= (1 << (const uint8_t)module);
114  PRINTF("LLWU_ME 0x%02x\n", LLWU->ME);
115  }
116 }
117 
118 void
119 llwu_disable_wakeup_module(const llwu_wakeup_module_t module)
120 {
121  if(module < LLWU_WAKEUP_MODULE_END) {
122  LLWU->ME &= ~(1 << (const uint8_t)module);
123  PRINTF("LLWU_ME 0x%02x\n", LLWU->ME);
124  }
125 }
126 
127 void
128 llwu_set_wakeup_pin(const llwu_wakeup_pin_t pin, const llwu_wakeup_edge_t edge)
129 {
130  uint8_t tmp;
131  if(pin >= LLWU_WAKEUP_PIN_END) {
132  /* invalid pin number */
133  DEBUGGER_BREAK(BREAK_INVALID_PARAM);
134  return;
135  }
136  /* LLWU pin enable registers are sequential in the address space */
137 
138  tmp = *(&LLWU->PE1 + (((const uint8_t)pin) / LLWU_WAKEUP_PIN_REG_SIZE));
139  /* Clear the bits in the field we want to modify */
140  tmp = (tmp & ~(LLWU_WAKEUP_EDGE_MASK << ((pin % LLWU_WAKEUP_PIN_REG_SIZE) *
141  LLWU_WAKEUP_EDGE_WIDTH)));
142  /* Set the new value */
143  tmp |= ((edge & LLWU_WAKEUP_EDGE_MASK) << ((pin % LLWU_WAKEUP_PIN_REG_SIZE) *
144  LLWU_WAKEUP_EDGE_WIDTH));
145  *(&LLWU->PE1 + (((const uint8_t)pin) / LLWU_WAKEUP_PIN_REG_SIZE)) = tmp;
146  PRINTF("LLTU 1 0x%02x\n", LLWU->PE1);
147  PRINTF("LLTU 2 0x%02x\n", LLWU->PE2);
148  PRINTF("LLTU 3 0x%02x\n", LLWU->PE3);
149  PRINTF("LLTU 4 0x%02x\n", LLWU->PE4);
150 }
151 
152 void __attribute__((interrupt))
153 _isr_llwu(void)
154 {
155  /* TODO(henrik) Dont know if this is really the correct way to handle the flags. */
156  /* Clear LLWU flags */
157  LLWU->F1 = 0xFF;
158  LLWU->F2 = 0xFF;
159  /* Read only register F3, the flag will need to be cleared in the peripheral
160  * instead of writing a 1 to the MWUFx bit. */
161  /* LLWU->F3 = 0xFF; */
162 }
Linked list manipulation routines.
__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
Enable External Interrupt.
Definition: core_cm0.h:535
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
enum llwu_wakeup_pin llwu_wakeup_pin_t
enum that maps physical pins to wakeup pin numbers in LLWU module
#define LLWU
Peripheral LLWU base pointer.
Definition: MK60D10.h:5321
void __attribute__((interrupt))
This ISR handles most of the business interacting with the 1-wire bus.
Definition: onewire.c:174
#define NULL
The null pointer.
#define SIM
Peripheral SIM base pointer.
Definition: MK60D10.h:7650
#define UART3
Peripheral UART3 base pointer.
Definition: MK60D10.h:8675
void llwu_sleep(void)
Sleep until some process is polled, ie interrupt occurs.
Definition: llwu.c:76
K60 hardware register header wrapper.
#define DEBUGGER_BREAK(sig)
Make the CPU signal to the debugger and break execution by issuing a bkpt instruction.
Definition: K60.h:164
void power_modes_init(void)
Definition: power-modes.c:62
void llwu_set_allow(llwu_control_t *c, char allow)
Method for a controller to allow or disallow deep sleep.
Definition: llwu.c:54
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
Provide common UART routines for MK60DZ10.
#define UART4
Peripheral UART4 base pointer.
Definition: MK60D10.h:8679
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
#define UART1
Peripheral UART1 base pointer.
Definition: MK60D10.h:8667
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
Power mode switching functions for the K60 CPU.
#define UART2
Peripheral UART2 base pointer.
Definition: MK60D10.h:8671
#define LIST(name)
Declare a linked list.
Definition: list.h:86
enum llwu_wakeup_module llwu_wakeup_module_t
Internal modules whose interrupts are mapped to LLWU wake up sources.
#define UART0
Peripheral UART0 base pointer.
Definition: MK60D10.h:8663
#define UART5
Peripheral UART5 base pointer.
Definition: MK60D10.h:8683
void llwu_register(llwu_control_t *c)
Register as a controller for llwu.
Definition: llwu.c:47
#define BITBAND_REG(Reg, Bit)
Macro to access a single bit of a peripheral register (bit band region 0x40000000 to 0x400FFFFF) usin...
Definition: MK60D10.h:71
Low Leakage Wakeup.
Definition: MK60D10.h:117